Load libraries

library(tidyverse) # for everything
library(readxl) # for reading in excel files
library(janitor) # data checks and cleaning
library(glue) # for easy pasting
library(FactoMineR) # for PCA
library(factoextra) # for PCA
library(rstatix) # for stats
library(pheatmap) # for heatmaps
library(plotly) # for interactive plots
library(htmlwidgets) # for saving interactive plots
library(devtools)
library(notame) # used for feature clustering
library(doParallel)
library(igraph) # feature clustering
library(ggpubr) # visualizations
library(knitr) # clean table printing
library(rmarkdown)
library(mixOmics) # for multilevel PCAs
library(pathview) # for functional analysis and KEGG annotation
library(ggcorrplot)
library(corrr)
library(ggthemes)

Read in data

# raw filtered metabolomics data in HILIC (+)
omicsdata <- read_csv("Feature lists/HILIC-POS-Filtered-Data-05Jun23_1444features.csv")

# metadata
metadata <- read_excel("Metadata-urine-HILIC-POS.xlsx")

Wrangle data

metadata <- metadata %>%
  rename("sample_ID" = Sample_ID)
# rename "row ID"
omicsdata <- omicsdata %>%
  rename("row_ID" = `row ID`)

# how many features
nrow(omicsdata)
## [1] 1444
# are there any duplicates?
omicsdata %>% get_dupes(mz_rt)
## # A tibble: 0 × 60
## # ℹ 60 variables: mz_rt <chr>, dupe_count <int>, row_ID <dbl>,
## #   6104_U2_HILICPOS_19 <dbl>, 6110_U2_HILICPOS_34 <dbl>,
## #   6108_U2_HILICPOS_27 <dbl>, 6111_U4_HILICPOS_42 <dbl>,
## #   6101_U3_HILICPOS_29 <dbl>, 6104_U1_HILICPOS_55 <dbl>,
## #   6103_U4_HILICPOS_53 <dbl>, 6110_U3_HILICPOS_45 <dbl>,
## #   6111_U2_HILICPOS_35 <dbl>, 6111_U1_HILICPOS_51 <dbl>,
## #   6105_U3_HILICPOS_39 <dbl>, 6109_U2_HILICPOS_13 <dbl>, …
# remove dupes
omicsdata <- omicsdata %>% 
  distinct(mz_rt, .keep_all = TRUE)

# check again for dupes
omicsdata %>% get_dupes(mz_rt)
## # A tibble: 0 × 60
## # ℹ 60 variables: mz_rt <chr>, dupe_count <int>, row_ID <dbl>,
## #   6104_U2_HILICPOS_19 <dbl>, 6110_U2_HILICPOS_34 <dbl>,
## #   6108_U2_HILICPOS_27 <dbl>, 6111_U4_HILICPOS_42 <dbl>,
## #   6101_U3_HILICPOS_29 <dbl>, 6104_U1_HILICPOS_55 <dbl>,
## #   6103_U4_HILICPOS_53 <dbl>, 6110_U3_HILICPOS_45 <dbl>,
## #   6111_U2_HILICPOS_35 <dbl>, 6111_U1_HILICPOS_51 <dbl>,
## #   6105_U3_HILICPOS_39 <dbl>, 6109_U2_HILICPOS_13 <dbl>, …
# how many features
nrow(omicsdata)
## [1] 1444

Sometimes a weird logical column (lgl) comes up in my data. Let’s check if it’s there

colnames(omicsdata)
##  [1] "mz_rt"                   "row_ID"                 
##  [3] "6104_U2_HILICPOS_19"     "6110_U2_HILICPOS_34"    
##  [5] "6108_U2_HILICPOS_27"     "6111_U4_HILICPOS_42"    
##  [7] "6101_U3_HILICPOS_29"     "6104_U1_HILICPOS_55"    
##  [9] "6103_U4_HILICPOS_53"     "6110_U3_HILICPOS_45"    
## [11] "6111_U2_HILICPOS_35"     "6111_U1_HILICPOS_51"    
## [13] "6105_U3_HILICPOS_39"     "6109_U2_HILICPOS_13"    
## [15] "6108_U1_HILICPOS_44"     "6109_U4_HILICPOS_22"    
## [17] "6101_U4_HILICPOS_14"     "6101_U2_HILICPOS_30"    
## [19] "6110_U4_HILICPOS_10"     "6105_U1_HILICPOS_43"    
## [21] "6105_U4_HILICPOS_47"     "6102_U1_HILICPOS_26"    
## [23] "6102_U2_HILICPOS_16"     "6104_U3_HILICPOS_32"    
## [25] "6104_U4_HILICPOS_12"     "6113_U3_HILICPOS_15"    
## [27] "6113_U2_HILICPOS_31"     "6109_U3_HILICPOS_52"    
## [29] "6102_U4_HILICPOS_50"     "6102_U3_HILICPOS_48"    
## [31] "6108_U4_HILICPOS_18"     "6106_U2_HILICPOS_46"    
## [33] "6111_U3_HILICPOS_54"     "6110_U1_HILICPOS_11"    
## [35] "6108_U3_HILICPOS_28"     "6106_U3_HILICPOS_20"    
## [37] "6105_U2_HILICPOS_40"     "6112_U2_HILICPOS_37"    
## [39] "6103_U1_HILICPOS_21"     "6113_U1_HILICPOS_24"    
## [41] "6103_U3_HILICPOS_61"     "6101_U1_HILICPOS_59"    
## [43] "6112_U1_HILICPOS_38"     "6109_U1_HILICPOS_58"    
## [45] "6112_U3_HILICPOS_56"     "6106_U4_HILICPOS_36"    
## [47] "6103_U2_HILICPOS_60"     "6106_U1_HILICPOS_23"    
## [49] "6112_U4_HILICPOS_63"     "PooledQC5_HILICPOS_11_1"
## [51] "6113_U4_HILICPOS_62"     "PooledQC7_HILICPOS_25"  
## [53] "PooledQC6_HILICPOS_17"   "PooledQC9_HILICPOS_41"  
## [55] "PooledQC10_HILICPOS_49"  "PooledQC8_HILICPOS_33"  
## [57] "PooledQC11_HILICPOS_57"  "PooledQC12_HILICPOS_64" 
## [59] "...59"
# remove weird lgl column
omicsdata <- omicsdata %>%
  dplyr::select(!where(is.logical))

colnames(omicsdata)
##  [1] "mz_rt"                   "row_ID"                 
##  [3] "6104_U2_HILICPOS_19"     "6110_U2_HILICPOS_34"    
##  [5] "6108_U2_HILICPOS_27"     "6111_U4_HILICPOS_42"    
##  [7] "6101_U3_HILICPOS_29"     "6104_U1_HILICPOS_55"    
##  [9] "6103_U4_HILICPOS_53"     "6110_U3_HILICPOS_45"    
## [11] "6111_U2_HILICPOS_35"     "6111_U1_HILICPOS_51"    
## [13] "6105_U3_HILICPOS_39"     "6109_U2_HILICPOS_13"    
## [15] "6108_U1_HILICPOS_44"     "6109_U4_HILICPOS_22"    
## [17] "6101_U4_HILICPOS_14"     "6101_U2_HILICPOS_30"    
## [19] "6110_U4_HILICPOS_10"     "6105_U1_HILICPOS_43"    
## [21] "6105_U4_HILICPOS_47"     "6102_U1_HILICPOS_26"    
## [23] "6102_U2_HILICPOS_16"     "6104_U3_HILICPOS_32"    
## [25] "6104_U4_HILICPOS_12"     "6113_U3_HILICPOS_15"    
## [27] "6113_U2_HILICPOS_31"     "6109_U3_HILICPOS_52"    
## [29] "6102_U4_HILICPOS_50"     "6102_U3_HILICPOS_48"    
## [31] "6108_U4_HILICPOS_18"     "6106_U2_HILICPOS_46"    
## [33] "6111_U3_HILICPOS_54"     "6110_U1_HILICPOS_11"    
## [35] "6108_U3_HILICPOS_28"     "6106_U3_HILICPOS_20"    
## [37] "6105_U2_HILICPOS_40"     "6112_U2_HILICPOS_37"    
## [39] "6103_U1_HILICPOS_21"     "6113_U1_HILICPOS_24"    
## [41] "6103_U3_HILICPOS_61"     "6101_U1_HILICPOS_59"    
## [43] "6112_U1_HILICPOS_38"     "6109_U1_HILICPOS_58"    
## [45] "6112_U3_HILICPOS_56"     "6106_U4_HILICPOS_36"    
## [47] "6103_U2_HILICPOS_60"     "6106_U1_HILICPOS_23"    
## [49] "6112_U4_HILICPOS_63"     "PooledQC5_HILICPOS_11_1"
## [51] "6113_U4_HILICPOS_62"     "PooledQC7_HILICPOS_25"  
## [53] "PooledQC6_HILICPOS_17"   "PooledQC9_HILICPOS_41"  
## [55] "PooledQC10_HILICPOS_49"  "PooledQC8_HILICPOS_33"  
## [57] "PooledQC11_HILICPOS_57"  "PooledQC12_HILICPOS_64"
# create long df for omics df
omicsdata_tidy <- omicsdata %>%
  pivot_longer(cols = 3:ncol(.),
               names_to = "sample_ID",
               values_to = "peak_height")

# combine meta and omics dfs
df_combined <- full_join(omicsdata_tidy,
                         metadata,
                         by = c("sample_ID" = "sample_ID"))

# separate mz and rt
df_combined_sep <- df_combined %>%
  separate(col = mz_rt,
           into = c("mz", "rt"),
           sep = "_") 

# convert columns to correct type
df_combined_sep$mz <- as.numeric(df_combined_sep$mz)
df_combined_sep$rt <- as.numeric(df_combined_sep$rt)
df_combined_sep$Subject <- as.character(df_combined_sep$Subject)
df_combined_sep$Intervention <- as.character(df_combined_sep$Intervention)

# rearrange column order
df_combined_sep <- df_combined_sep %>%
  dplyr::select(sample_ID, pre_post, Intervention, everything())

str(df_combined_sep)
## tibble [80,864 × 14] (S3: tbl_df/tbl/data.frame)
##  $ sample_ID        : chr [1:80864] "6104_U2_HILICPOS_19" "6110_U2_HILICPOS_34" "6108_U2_HILICPOS_27" "6111_U4_HILICPOS_42" ...
##  $ pre_post         : chr [1:80864] "post" "post" "post" "post" ...
##  $ Intervention     : chr [1:80864] "Yellow" "Yellow" "Red" "Red" ...
##  $ mz               : num [1:80864] 553 553 553 553 553 ...
##  $ rt               : num [1:80864] 0.421 0.421 0.421 0.421 0.421 0.421 0.421 0.421 0.421 0.421 ...
##  $ row_ID           : num [1:80864] 152 152 152 152 152 152 152 152 152 152 ...
##  $ peak_height      : num [1:80864] 3029 4240 2162 8723 3652 ...
##  $ Subject          : chr [1:80864] "6104" "6110" "6108" "6111" ...
##  $ Period           : chr [1:80864] "U2" "U2" "U2" "U4" ...
##  $ sequence         : chr [1:80864] "Y_R" "Y_R" "R_Y" "Y_R" ...
##  $ Intervention_week: num [1:80864] 6 6 6 14 10 2 14 10 6 2 ...
##  $ Sex              : chr [1:80864] "M" "M" "M" "M" ...
##  $ Age              : num [1:80864] 54 36 68 46 58 54 60 36 46 46 ...
##  $ BMI              : num [1:80864] 33.1 29.9 31.8 30 31.1 ...
# replace NA's in subject and intervention columns with QC
df_combined_sep$Subject <- df_combined_sep$Subject %>%
  replace_na("QC")

df_combined_sep$Intervention <- df_combined_sep$Intervention %>%
  replace_na("QC")

Data summaries

Number of masses detected

nrow(omicsdata)
## [1] 1444

Mass range for metabolites detected?

range(df_combined_sep$mz)
## [1]  61.0395 975.5183

RT range for metabolites detected?

range(df_combined_sep$rt)
## [1] 0.421 9.502

mass vs RT scatterplot

# plot
(plot_mzvsrt <- df_combined_sep %>%
  ggplot(aes(x = rt, y = mz)) +
  geom_point() +
  theme_minimal() +
  labs(x = "Retention time, min",
       y = "m/z, neutral",
       title = "mz across RT for all features"))

Histogram for mass range

df_combined_sep %>%
  ggplot(aes(x = mz)) +
  geom_histogram(binwidth = 25) +
  theme_minimal() +
  labs(x = "Monoisotopic mass (amu)",
       y = "Number of features",
       title = "Distribution of features by mass")

Histogram for RT

df_combined_sep %>%
  ggplot(aes(x = rt)) +
  geom_histogram(binwidth = 0.1) + # 6 second bins
  theme_minimal() +
  labs(x = "Retention time",
       y = "Number of features",
       title = "Distribution of features by retention time")

NAs and imputing

NAs

# NAs in all data including QCs
NAbyRow <- rowSums(is.na(omicsdata[,-1]))

hist(NAbyRow,
     breaks = 56, # because there are 56 samples, 48 samples + 8 QCs
     xlab = "Number of missing values",
     ylab = "Number of metabolites",
     main = "How many missing values are there?")

# samples only (no QCs)
omicsdata_noQC <- omicsdata %>%
  dplyr::select(-contains("QC"))

#NAs in samples only?
NAbyRow_noQC <- rowSums(is.na(omicsdata_noQC[,-1]))

hist(NAbyRow_noQC,
     breaks = 48, # because there are 48 samples 
     xlab = "Number of missing values",
     ylab = "Number of metabolites",
     main = "How many missing values are there?")

Are there any missing values in QCs? There shouldn’t be after data preprocessing/filtering

omicsdata_QC <- omicsdata %>%
  dplyr::select(starts_with("P")) 

NAbyRow_QC <- colSums(is.na(omicsdata_QC))
# lets confirm that there are no missing values from my QCs
sum(NAbyRow_QC) # no
## [1] 0
# calculate how many NAs there are per feature in whole data set
contains_NAs <- df_combined %>%
  group_by(mz_rt) %>%
  count(is.na(peak_height)) %>%
  filter(`is.na(peak_height)` == TRUE)
head(contains_NAs)
## # A tibble: 6 × 3
## # Groups:   mz_rt [6]
##   mz_rt          `is.na(peak_height)`     n
##   <chr>          <lgl>                <int>
## 1 111.0438_5.483 TRUE                    44
## 2 121.0283_5.484 TRUE                    44
## 3 121.0283_5.936 TRUE                    43
## 4 121.0285_7.146 TRUE                    22
## 5 121.0684_7.945 TRUE                     1
## 6 122.0634_4.935 TRUE                     3

Remove NAs

Update May 3, 2023: I’ve been seeing outliers in unsupervised analyses. So to handle this, I think it is best to filter out metabolites that are only present in one person. So I will remove metabolites that are missing from at least 44 samples. Taking this out for now.

# remove features that have 44 or more NAs
omit_features <- contains_NAs %>%
  filter(n >= 44)
#preview
nrow(omit_features) # 109 features to remove

# how many features to remove?
nrow(omicsdata) - nrow(omit_features)

# now remove these features from the omics dataset
omicsdata <- omicsdata %>%
  anti_join(omit_features,
            by = "mz_rt")

 # how many features are there now?
nrow(omicsdata)

Data imputation

# impute any missing values by replacing them with 1/2 of the lowest peak height value of a feature (i.e. in a row).
imputed_omicsdata <- omicsdata

imputed_omicsdata[] <- lapply(imputed_omicsdata, 
                              function(x) ifelse(is.na(x),
                                                 min(x, na.rm = TRUE)/2, x))

dim(imputed_omicsdata)
## [1] 1444   58

Are there any NAs?

imputed_omicsdata %>%
  is.na() %>%
  sum()
## [1] 0
# imputations worked

Create new imputed tidy datasets

# create long df for imputed omics df
imputed_omicsdata_tidy <- imputed_omicsdata %>%
  pivot_longer(cols = 3:ncol(.),
               names_to = "sample_ID",
               values_to = "peak_height")

# combine meta and imputed omics dfs
imputed_fulldata <- full_join(imputed_omicsdata_tidy,
                         metadata,
                         by = c("sample_ID" = "sample_ID"))

# separate mz and rt
imputed_fulldata_sep <- imputed_fulldata %>%
  separate(col = mz_rt,
           into = c("mz", "rt"),
           sep = "_") 

# convert columns to correct type
imputed_fulldata_sep$mz <- as.numeric(imputed_fulldata_sep$mz)
imputed_fulldata_sep$rt <- as.numeric(imputed_fulldata_sep$rt)
imputed_fulldata_sep$Subject <- as.character(imputed_fulldata_sep$Subject)
imputed_fulldata_sep$Intervention <- as.character(imputed_fulldata_sep$Intervention)

Notame feature reduction

vignette for reference

#browseVignettes("notame")

Plot features. RT vs mz before notame

# rt vs mz plot
imputed_fulldata_sep %>%
  ggplot(aes(x = rt, y = mz)) +
  geom_point() +
  theme_minimal() +
  labs(x = "RT (min)",
       y = "mz")

Data restructuring for notame

# create features list from imputed data set to only include unique feature ID's (mz_rt), mz and RT
features <- imputed_fulldata_sep %>%
  cbind(imputed_fulldata$mz_rt) %>%
  rename("mz_rt" = "imputed_fulldata$mz_rt") %>%
  dplyr::select(c(mz_rt, mz, rt)) %>%
  distinct() # remove the duplicate rows

# create a second data frame which is just imputed_fulldata restructured to another wide format
data_notame <- data.frame(imputed_omicsdata %>%
                            dplyr::select(-row_ID) %>%
                            t())

data_notame <- data_notame %>%
  tibble::rownames_to_column() %>% # change samples from rownames to its own column
  row_to_names(row_number = 1) # change the feature IDs (mz_rt) from first row obs into column names

Check structures

# check if mz and rt are numeric
str(features)
## 'data.frame':    1444 obs. of  3 variables:
##  $ mz_rt: chr  "553.3887_0.421" "666.637_0.431" "489.2284_0.496" "696.649_0.442" ...
##  $ mz   : num  553 667 489 697 297 ...
##  $ rt   : num  0.421 0.431 0.496 0.442 0.496 0.463 0.501 0.456 0.467 0.471 ...
tibble(features)
## # A tibble: 1,444 × 3
##    mz_rt             mz    rt
##    <chr>          <dbl> <dbl>
##  1 553.3887_0.421  553. 0.421
##  2 666.637_0.431   667. 0.431
##  3 489.2284_0.496  489. 0.496
##  4 696.649_0.442   697. 0.442
##  5 297.1938_0.496  297. 0.496
##  6 356.2792_0.463  356. 0.463
##  7 349.1768_0.501  349. 0.501
##  8 376.3184_0.456  376. 0.456
##  9 572.3994_0.467  572. 0.467
## 10 188.0705_0.471  188. 0.471
## # ℹ 1,434 more rows
# check if results are numeric
tibble(data_notame)
## # A tibble: 56 × 1,445
##    mz_rt       `553.3887_0.421` `666.637_0.431` `489.2284_0.496` `696.649_0.442`
##    <chr>       <chr>            <chr>           <chr>            <chr>          
##  1 6104_U2_HI… "   3028.5269"   "  20290.4630"  "   1683.3690"   "   4875.8610" 
##  2 6110_U2_HI… "   4240.2890"   "    501.9858"  "   2050.1914"   "    501.9858" 
##  3 6108_U2_HI… "   2162.1748"   "    505.4764"  "    505.4764"   "   1630.4479" 
##  4 6111_U4_HI… "   8723.3040"   "   1186.7206"  "   1205.0459"   "   1212.5492" 
##  5 6101_U3_HI… "   3652.0537"   "   1152.5675"  "    500.2496"   "   1005.7766" 
##  6 6104_U1_HI… "   6700.4805"   "   1479.3018"  "   1635.1193"   "  13722.4230" 
##  7 6103_U4_HI… "   7693.8310"   "   3270.3160"  "    501.4131"   "    501.4131" 
##  8 6110_U3_HI… "   1657.042"    "    500.043"   "   1443.855"    "   1172.850"  
##  9 6111_U2_HI… "   7980.3076"   "   1054.9924"  "   1205.5146"   "   1201.1527" 
## 10 6111_U1_HI… "   4490.8410"   "   1628.5261"  "   2146.6333"   "   1494.4916" 
## # ℹ 46 more rows
## # ℹ 1,440 more variables: `297.1938_0.496` <chr>, `356.2792_0.463` <chr>,
## #   `349.1768_0.501` <chr>, `376.3184_0.456` <chr>, `572.3994_0.467` <chr>,
## #   `188.0705_0.471` <chr>, `342.2636_0.484` <chr>, `357.1666_0.506` <chr>,
## #   `313.1894_0.512` <chr>, `324.2159_0.482` <chr>, `472.3114_0.479` <chr>,
## #   `568.4552_0.447` <chr>, `169.0495_0.477` <chr>, `328.248_0.503` <chr>,
## #   `456.3161_0.48` <chr>, `540.4236_0.454` <chr>, `292.2058_0.518` <chr>, …
# change to results to numeric
data_notame <- data_notame %>%
  mutate_at(-1, as.numeric)

tibble(data_notame)
## # A tibble: 56 × 1,445
##    mz_rt       `553.3887_0.421` `666.637_0.431` `489.2284_0.496` `696.649_0.442`
##    <chr>                  <dbl>           <dbl>            <dbl>           <dbl>
##  1 6104_U2_HI…            3029.          20290.            1683.           4876.
##  2 6110_U2_HI…            4240.            502.            2050.            502.
##  3 6108_U2_HI…            2162.            505.             505.           1630.
##  4 6111_U4_HI…            8723.           1187.            1205.           1213.
##  5 6101_U3_HI…            3652.           1153.             500.           1006.
##  6 6104_U1_HI…            6700.           1479.            1635.          13722.
##  7 6103_U4_HI…            7694.           3270.             501.            501.
##  8 6110_U3_HI…            1657.            500.            1444.           1173.
##  9 6111_U2_HI…            7980.           1055.            1206.           1201.
## 10 6111_U1_HI…            4491.           1629.            2147.           1494.
## # ℹ 46 more rows
## # ℹ 1,440 more variables: `297.1938_0.496` <dbl>, `356.2792_0.463` <dbl>,
## #   `349.1768_0.501` <dbl>, `376.3184_0.456` <dbl>, `572.3994_0.467` <dbl>,
## #   `188.0705_0.471` <dbl>, `342.2636_0.484` <dbl>, `357.1666_0.506` <dbl>,
## #   `313.1894_0.512` <dbl>, `324.2159_0.482` <dbl>, `472.3114_0.479` <dbl>,
## #   `568.4552_0.447` <dbl>, `169.0495_0.477` <dbl>, `328.248_0.503` <dbl>,
## #   `456.3161_0.48` <dbl>, `540.4236_0.454` <dbl>, `292.2058_0.518` <dbl>, …

Find connections

connection <- find_connections(data = data_notame,
                               features = features,
                               corr_thresh = 0.9,
                               rt_window = 1/60,
                               name_col = "mz_rt",
                               mz_col = "mz",
                               rt_col = "rt")
## [1] 100
## [1] 200
## [1] 300
## [1] 400
## [1] 500
## [1] 600
## [1] 700
## [1] 800
## [1] 900
## [1] 1000
## [1] 1100
## [1] 1200
## [1] 1300
## [1] 1400
head(connection)
##                x              y       cor rt_diff   mz_diff
## 1 489.2284_0.496 644.3586_0.492 0.9693076  -0.004  155.1302
## 2 572.3994_0.467  456.3161_0.48 0.9617216   0.013 -116.0833
## 3 313.1894_0.512 270.1866_0.504 0.9164155  -0.008  -43.0028
## 4 313.1894_0.512 244.1663_0.521 0.9075760   0.009  -69.0231
## 5 313.1894_0.512 522.2943_0.527 0.9014331   0.015  209.1049
## 6 568.4552_0.447 540.4236_0.454 0.9884175   0.007  -28.0316

Clustering

clusters <- find_clusters(connections = connection, d_thresh = 0.8)
## 167 components found
## 
## Component 100 / 167 
## 50 components found
## 
## 12 components found
## 
## 3 components found
# assign a cluster ID to all features. Clusters are named after feature with highest median peak height
features_clustered <- assign_cluster_id(data_notame, clusters, features, name_col = "mz_rt")


# visualize clusters
#visualize_clusters(data_notame, features, clusters, min_size = 3, rt_window = 2,name_col = "mz_rt", mz_col = "mz", rt_col = "rt", file_path = "~/path/to/project/")

# lets see how many features are removed when we only keep one feature per cluster
pulled <- pull_clusters(data_notame, features_clustered, name_col = "mz_rt")
cluster_data <- pulled$cdata

cluster_features <- pulled$cfeatures
# export clustered feature list
write_csv(cluster_features,
          "cluster_features-_hilic-pos.csv")

nrow(omicsdata) - nrow(cluster_features)
## [1] 363

Reduce dataset based on clustering

# transpose the full dataset back to wide so that it is more similar to the notame dataset
imputed_fulldata_wide <- imputed_fulldata %>%
  dplyr::select(-"row_ID") %>%
  pivot_wider(names_from = mz_rt,
              values_from = peak_height)

# list of reduced features
clusternames <- cluster_features$mz_rt

# dplyr::select only the features are in the reduced list
imp_clust <- imputed_fulldata_wide[,c(names(imputed_fulldata_wide) %in% clusternames)]

# bind back sample names
imp_clust <- cbind(imputed_fulldata_wide[1], imp_clust)

tibble(imp_clust)
## # A tibble: 56 × 1,082
##    sample_ID   `553.3887_0.421` `666.637_0.431` `489.2284_0.496` `696.649_0.442`
##    <chr>                  <dbl>           <dbl>            <dbl>           <dbl>
##  1 6104_U2_HI…            3029.          20290.            1683.           4876.
##  2 6110_U2_HI…            4240.            502.            2050.            502.
##  3 6108_U2_HI…            2162.            505.             505.           1630.
##  4 6111_U4_HI…            8723.           1187.            1205.           1213.
##  5 6101_U3_HI…            3652.           1153.             500.           1006.
##  6 6104_U1_HI…            6700.           1479.            1635.          13722.
##  7 6103_U4_HI…            7694.           3270.             501.            501.
##  8 6110_U3_HI…            1657.            500.            1444.           1173.
##  9 6111_U2_HI…            7980.           1055.            1206.           1201.
## 10 6111_U1_HI…            4491.           1629.            2147.           1494.
## # ℹ 46 more rows
## # ℹ 1,077 more variables: `297.1938_0.496` <dbl>, `356.2792_0.463` <dbl>,
## #   `349.1768_0.501` <dbl>, `376.3184_0.456` <dbl>, `572.3994_0.467` <dbl>,
## #   `188.0705_0.471` <dbl>, `342.2636_0.484` <dbl>, `357.1666_0.506` <dbl>,
## #   `313.1894_0.512` <dbl>, `324.2159_0.482` <dbl>, `472.3114_0.479` <dbl>,
## #   `169.0495_0.477` <dbl>, `328.248_0.503` <dbl>, `540.4236_0.454` <dbl>,
## #   `269.1382_0.482` <dbl>, `339.1871_0.5` <dbl>, `316.2478_0.506` <dbl>, …

Mz vs RT scatterplot

# plot new rt vs mz scatterplot post-clustering
(plot_mzvsrt_postcluster <- cluster_features %>%
  ggplot(aes(x = rt,
             y = mz)) +
  geom_point() +
  theme_minimal() +
  labs(x = "Retention time, min",
       y = "m/z, neutral",
       title = "mz across RT for all features after clustering"))

# plot both scatterplots to compare with and without notame clustering
(scatterplots <- ggarrange(plot_mzvsrt, 
                           plot_mzvsrt_postcluster, 
                           nrow = 2))

Bind meta data

imp_metabind_clust <- right_join(metadata, 
                                 imp_clust,
                                 by = "sample_ID")

Visualize untransformed data

Data wrangling

# change meta data columns to character so that I can change NAs from QCs to "QC"
imp_metabind_clust <- imp_metabind_clust %>%
  mutate_at(c("Subject",
              "Period",
              "Intervention",
              "pre_post",
              "sequence",
              "Intervention_week",
              "Sex",
              "Age",
              "BMI"),
            as.character) 

# replace NAs in metadata columns for QCs
imp_metabind_clust[is.na(imp_metabind_clust)] <- "QC"

# unite pre_post column with intervention column to create pre_intervention column
imp_metabind_clust <- imp_metabind_clust %>%
  unite(col = "pre_post_intervention",
        c("pre_post","Intervention"),
        sep = "_",
        remove = FALSE)

# long df
imp_metabind_clust_tidy <- imp_metabind_clust %>%
  pivot_longer(cols = 12:ncol(.),
               names_to = "mz_rt",
               values_to = "rel_abund")

# structure check
str(imp_metabind_clust_tidy)
## tibble [60,536 × 13] (S3: tbl_df/tbl/data.frame)
##  $ sample_ID            : chr [1:60536] "6101_U1_HILICPOS_59" "6101_U1_HILICPOS_59" "6101_U1_HILICPOS_59" "6101_U1_HILICPOS_59" ...
##  $ Subject              : chr [1:60536] "6101" "6101" "6101" "6101" ...
##  $ Period               : chr [1:60536] "U1" "U1" "U1" "U1" ...
##  $ pre_post_intervention: chr [1:60536] "pre_Red" "pre_Red" "pre_Red" "pre_Red" ...
##  $ Intervention         : chr [1:60536] "Red" "Red" "Red" "Red" ...
##  $ pre_post             : chr [1:60536] "pre" "pre" "pre" "pre" ...
##  $ sequence             : chr [1:60536] "R_Y" "R_Y" "R_Y" "R_Y" ...
##  $ Intervention_week    : chr [1:60536] "2" "2" "2" "2" ...
##  $ Sex                  : chr [1:60536] "F" "F" "F" "F" ...
##  $ Age                  : chr [1:60536] "58" "58" "58" "58" ...
##  $ BMI                  : chr [1:60536] "31.0556029483653" "31.0556029483653" "31.0556029483653" "31.0556029483653" ...
##  $ mz_rt                : chr [1:60536] "553.3887_0.421" "666.637_0.431" "489.2284_0.496" "696.649_0.442" ...
##  $ rel_abund            : num [1:60536] 4290 5838 503 45615 2919 ...

Boxplot

imp_metabind_clust_tidy %>%
  ggplot(aes(x = sample_ID, y = rel_abund, color = Intervention)) +
  geom_boxplot(alpha = 0.6) +
  scale_color_manual(values = c("light grey", "tomato1", "gold")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90)) +
  labs(title = "LC-MS (+) Feature Abundances by Sample",
       subtitle = "Unscaled data",
       y = "Relative abundance")

Will need to log transform in order to normalize and actually see the data

Log2 transform

imp_metabind_clust_tidy_log2 <- imp_metabind_clust_tidy %>%
  mutate(rel_abund_log2 = if_else(rel_abund > 0, log2(rel_abund), 0)) %>%
  replace(is.na(.), 0)

Boxplot

(bp_data_quality <- imp_metabind_clust_tidy_log2 %>%
  ggplot(aes(x = sample_ID, y = rel_abund_log2, fill = Intervention)) +
  geom_boxplot(alpha = 0.6) +
  scale_fill_manual(values = c("light grey", "tomato1", "gold")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90)) +
  labs(title = "LC-MS (+) Feature Abundances by Sample",
       subtitle = "Log2 transformed data",
       y = "Relative abundance"))

Much better! QCs look like there may be some drift though. Let’s do drift correction with the Notame package!

Notame drift correction

Data wrangling

Feature abund df

# filtered and imputed data after notame clustering, transposed
features_testforQCcorr <- t(imp_clust) %>%
  as.data.frame() %>%
  row_to_names(row_number = "find_header")

# log2 transform
log2_features_testforQCcorr <- features_testforQCcorr %>%
  mutate_all(as.numeric) %>%
  log2()
# write csv to manually edit
write.csv(log2_features_testforQCcorr,
          "notame dfs/feaures_test.csv",
          row.names = TRUE)

Import corrected df (edited so that mz_rt could rowname 1)

features_forQCcorr <- read.csv("notame dfs/features_forQCcorr.csv",
                                header = FALSE,
                                row.names = 1)


features_forQCcorr <- features_forQCcorr %>%
  rownames_to_column(var = "mz_rt") %>%
  row_to_names(row_number = 1)%>%
  separate(col = mz_rt,
           into = c("mz", "rt"),
           sep = "_")
write.csv(features_forQCcorr,
          "notame dfs/features_forQCcorr_pt2.csv",
          row.names = TRUE)

Pheno df

# separate sampleID and injection order
pheno_data <- imp_clust[1] %>%
  separate(col = sample_ID,
           into = c("sample_ID", "injection_order"),
           sep = "_HILICPOS_")

# change pre-run QC injection number to correct number
pheno_data[48, "injection_order"] <- 9

# make inj order column num
pheno_data <- pheno_data %>%
  mutate_at("injection_order", as.numeric)

t_pheno_data <- as.data.frame(t(pheno_data))

Combine pheno and feature dfs manually in excel to create metaboset df.

write.csv(t_pheno_data,
          "notame dfs/pheno_df.csv",
          row.names = TRUE)

Import Metaboset

#make sure when converting csv to xlsx that you save as a new file, don't just change the name of the file
metaboset <- read_from_excel("notame dfs/metaboset.xlsx",
                             split_by = c("column", "Ion mode"))
## INFO [2024-05-22 16:49:14] Detecting corner position
## INFO [2024-05-22 16:49:14] Corner detected correctly at row 3, column D
## INFO [2024-05-22 16:49:14] 
## Extracting sample information from rows 1 to 3 and columns E to BH
## INFO [2024-05-22 16:49:14] Replacing spaces in sample information column names with underscores (_)
## INFO [2024-05-22 16:49:14] Naming the last column of sample information "Datafile"
## INFO [2024-05-22 16:49:14] 
## Extracting feature information from rows 4 to 1084 and columns A to D
## INFO [2024-05-22 16:49:14] Creating Split column from column, Ion mode
## INFO [2024-05-22 16:49:14] Feature_ID column not found, creating feature IDs
## INFO [2024-05-22 16:49:14] Identified m/z column mass and retention time column rt
## INFO [2024-05-22 16:49:14] Creating feature IDs from Split, m/z and retention time
## INFO [2024-05-22 16:49:14] Replacing dots (.) in feature information column names with underscores (_)
## INFO [2024-05-22 16:49:14] 
## Extracting feature abundances from rows 4 to 1084 and columns E to BH
## INFO [2024-05-22 16:49:14] 
## Checking sample information
## INFO [2024-05-22 16:49:14] QC column generated from rows containing 'QC'
## INFO [2024-05-22 16:49:14] Sample ID autogenerated from injection orders and prefix ID_
## INFO [2024-05-22 16:49:14] Checking that feature abundances only contain numeric values
## INFO [2024-05-22 16:49:14] 
## Checking feature information
## INFO [2024-05-22 16:49:14] Checking that feature IDs are unique and not stored as numbers
## INFO [2024-05-22 16:49:14] Checking that m/z and retention time values are reasonable
#construct Metaboset
modes <- construct_metabosets(exprs = metaboset$exprs,
                              pheno_data = metaboset$pheno_data,
                              feature_data = metaboset$feature_data, group_col = "Class")
## Initializing the object(s) with unflagged features
## INFO [2024-05-22 16:49:14] 
## Checking feature information
## INFO [2024-05-22 16:49:14] Checking that feature IDs are unique and not stored as numbers
## INFO [2024-05-22 16:49:14] Checking that feature abundances only contain numeric values
## INFO [2024-05-22 16:49:14] Setting row and column names of exprs based on feature and pheno data
#extract each mode into a single object
mode <- modes$HILIC_pos

Boxplots before correction

(qualityBPs_b4correction <- plot_sample_boxplots(mode, order_by = "Class", title = "Uncorrected feature abundance"))

Boxplots after QC drift correction

drift corrected takes up to 2 minutes

mode <- flag_detection(mode, qc_limit = 0.75, group_limit = 0.8)
## INFO [2024-05-22 16:49:18] 
## 0% of features flagged for low detection rate
corrected <- correct_drift(mode, log_transform = FALSE)
## INFO [2024-05-22 16:49:18] 
## Starting drift correction at 2024-05-22 16:49:18.962368
## INFO [2024-05-22 16:49:23] Drift correction performed at 2024-05-22 16:49:23.533239
## INFO [2024-05-22 16:49:25] Inspecting drift correction results 2024-05-22 16:49:25.29982
## INFO [2024-05-22 16:49:28] Drift correction results inspected at 2024-05-22 16:49:28.312032
## INFO [2024-05-22 16:49:28] 
## Drift correction results inspected, report:
## Drift_corrected: 100%

Did drift correction work?

inspection also takes about 2 minutes to run; output is percent of the features that were drift corrected. The remaining “low-quality” percent represents features for which the DC did not improve the RSD and D-ratio of the original data.

inspected <- inspect_dc(orig = mode, dc = corrected, check_quality = TRUE)
## INFO [2024-05-22 16:49:29] Inspecting drift correction results 2024-05-22 16:49:29.821425
## INFO [2024-05-22 16:49:39] Drift correction results inspected at 2024-05-22 16:49:39.792866
## INFO [2024-05-22 16:49:39] 
## Drift correction results inspected, report:
## Drift_corrected: 74%,  Low_quality: 26%

Boxplots, corrected

(qualityBPS_driftcorrection <- plot_sample_boxplots(corrected, order_by = "Class", title = "Corrected feature abundance"))

Compare quality BPs

(qualityBPs_compared <- ggarrange(qualityBPs_b4correction, qualityBPS_driftcorrection,
                    ncol = 1, nrow = 2))

Export new Metaboset to Excel spreadsheet

write_to_excel(corrected, "notame dfs/metaboset_corrected.xlsx")

Import edited Metaboset

metabdata_corrected <- read.csv(file = "notame dfs/metaboset_corrected_editedforR.csv",
                                check.names = FALSE)

Wrangle new metab data

Combine mz & rt back together

metabdata_corrected_MZ_RT <- metabdata_corrected %>%
  mutate(mass = round(metabdata_corrected$mass, digits = 4), # Decrease number of decimals for m/z & rt
         rt = round(metabdata_corrected$rt, digits = 3),
         .before=1,
         .keep="unused") %>%
  unite(mz_rt, c(mass, rt), remove=TRUE) # Combine m/z & rt with _ in between

Transpose new df

metabdata_corrected_t <- as.data.frame(t(metabdata_corrected_MZ_RT)) %>%
  row_to_names(row_number = "find_header") %>% # make MZ_RT column names
  rownames_to_column(var = "subj_period") # change rownames to column 1

Bind new data with metadata

I want the new drift corrected (DC) df to look just like “imp_metabind_clust_log2” df

# go back to wide data
imp_metabind_clust_log2 <- imp_metabind_clust_tidy_log2 %>%
  dplyr::select(!rel_abund) %>%
  pivot_wider(names_from = mz_rt,
              values_from = rel_abund_log2)
# combine subject and period columns from imp_metabind_clust_log2 in order to mimic DC df
subj_per_imp_metabind_clust_log2 <- imp_metabind_clust_log2 %>%
  unite(subj_period, c(Subject, Period), remove = FALSE)

# place new DC observations in
DC_imp_metabind_clust_log2 <- full_join(subj_per_imp_metabind_clust_log2[,c(1:12)], metabdata_corrected_t, by = "subj_period")

# take out old QC observations
DC_imp_metabind_clust_log2 <- DC_imp_metabind_clust_log2 %>%
  filter(subj_period != "QC_QC")

# replace NAs in columns for QCs
DC_imp_metabind_clust_log2[is.na(DC_imp_metabind_clust_log2)] <- "QC"

PCAs

With QCS

Wrangle

Data after drift correction

DC_imp_metabind_clust_log2 <- DC_imp_metabind_clust_log2 %>%
  dplyr::select(!"subj_period") %>%
  mutate_at(-c(1:11), as.numeric)
PCA.DC_imp_metabind_clust_log2 <- PCA(DC_imp_metabind_clust_log2,  # wide data
                                   quali.sup = 1:11, # remove qualitative variables
                                   graph = FALSE, # don't graph
                                   scale.unit = FALSE) # don't scale, already transformed data

# PCA summary
kable(summary(PCA.DC_imp_metabind_clust_log2))
## 
## Call:
## PCA(X = DC_imp_metabind_clust_log2, scale.unit = FALSE, quali.sup = 1:11,  
##      graph = FALSE) 
## 
## 
## Eigenvalues
##                        Dim.1   Dim.2   Dim.3   Dim.4   Dim.5   Dim.6   Dim.7
## Variance             740.319 277.768 170.943 111.555 103.715  76.395  71.350
## % of var.             34.724  13.028   8.018   5.232   4.865   3.583   3.347
## Cumulative % of var.  34.724  47.752  55.770  61.002  65.867  69.450  72.797
##                        Dim.8   Dim.9  Dim.10  Dim.11  Dim.12  Dim.13  Dim.14
## Variance              54.362  46.711  43.259  42.936  34.019  32.825  24.354
## % of var.              2.550   2.191   2.029   2.014   1.596   1.540   1.142
## Cumulative % of var.  75.347  77.537  79.566  81.580  83.176  84.716  85.858
##                       Dim.15  Dim.16  Dim.17  Dim.18  Dim.19  Dim.20  Dim.21
## Variance              23.286  19.938  19.341  17.074  15.831  13.860  13.390
## % of var.              1.092   0.935   0.907   0.801   0.743   0.650   0.628
## Cumulative % of var.  86.950  87.885  88.792  89.593  90.336  90.986  91.614
##                       Dim.22  Dim.23  Dim.24  Dim.25  Dim.26  Dim.27  Dim.28
## Variance              13.061  12.009  11.811  10.464   9.839   8.905   8.619
## % of var.              0.613   0.563   0.554   0.491   0.461   0.418   0.404
## Cumulative % of var.  92.226  92.790  93.344  93.834  94.296  94.714  95.118
##                       Dim.29  Dim.30  Dim.31  Dim.32  Dim.33  Dim.34  Dim.35
## Variance               7.911   7.585   7.007   6.857   6.548   5.948   5.696
## % of var.              0.371   0.356   0.329   0.322   0.307   0.279   0.267
## Cumulative % of var.  95.489  95.845  96.173  96.495  96.802  97.081  97.348
##                       Dim.36  Dim.37  Dim.38  Dim.39  Dim.40  Dim.41  Dim.42
## Variance               5.555   5.404   5.080   4.891   4.629   4.614   4.341
## % of var.              0.261   0.253   0.238   0.229   0.217   0.216   0.204
## Cumulative % of var.  97.609  97.862  98.101  98.330  98.547  98.764  98.967
##                       Dim.43  Dim.44  Dim.45  Dim.46  Dim.47  Dim.48  Dim.49
## Variance               3.933   3.790   3.463   3.328   2.998   2.653   0.425
## % of var.              0.184   0.178   0.162   0.156   0.141   0.124   0.020
## Cumulative % of var.  99.152  99.329  99.492  99.648  99.789  99.913  99.933
##                       Dim.50  Dim.51  Dim.52  Dim.53  Dim.54  Dim.55
## Variance               0.407   0.341   0.320   0.259   0.103   0.000
## % of var.              0.019   0.016   0.015   0.012   0.005   0.000
## Cumulative % of var.  99.952  99.968  99.983  99.995 100.000 100.000
## 
## Individuals (the 10 first)
##                                    Dist     Dim.1     ctr    cos2     Dim.2
## 1                             |  36.227 | -21.292   1.094   0.345 |  -4.578
## 2                             |  32.026 | -19.203   0.890   0.360 |  -4.873
## 3                             |  41.195 | -11.622   0.326   0.080 |  -7.221
## 4                             |  36.311 | -21.504   1.115   0.351 |  -3.138
## 5                             |  37.947 | -19.379   0.906   0.261 |  -2.619
## 6                             |  72.463 |  56.513   7.703   0.608 | -32.698
## 7                             |  42.209 | -17.833   0.767   0.179 |  -2.702
## 8                             |  37.900 | -12.342   0.367   0.106 |  -7.527
## 9                             |  33.967 | -13.714   0.454   0.163 |  -7.331
## 10                            |  38.441 | -12.606   0.383   0.108 |  -0.274
##                                   ctr    cos2     Dim.3     ctr    cos2  
## 1                               0.135   0.016 |   4.134   0.179   0.013 |
## 2                               0.153   0.023 |  -2.010   0.042   0.004 |
## 3                               0.335   0.031 |  -1.279   0.017   0.001 |
## 4                               0.063   0.007 |   2.512   0.066   0.005 |
## 5                               0.044   0.005 |  -1.708   0.030   0.002 |
## 6                               6.873   0.204 | -10.672   1.190   0.022 |
## 7                               0.047   0.004 |  -0.190   0.000   0.000 |
## 8                               0.364   0.039 |  -4.555   0.217   0.014 |
## 9                               0.346   0.047 |   5.926   0.367   0.030 |
## 10                              0.000   0.000 |  -5.964   0.372   0.024 |
## 
## Variables (the 10 first)
##                                  Dim.1    ctr   cos2    Dim.2    ctr   cos2  
## 553.3887_0.421                |  0.274  0.010  0.046 |  0.483  0.084  0.144 |
## 666.637_0.431                 |  0.199  0.005  0.044 |  0.070  0.002  0.005 |
## 489.2284_0.496                |  1.344  0.244  0.321 |  1.525  0.837  0.413 |
## 696.649_0.442                 |  0.096  0.001  0.005 |  0.274  0.027  0.039 |
## 297.1938_0.496                |  1.715  0.397  0.886 |  0.003  0.000  0.000 |
## 356.2792_0.463                | -0.107  0.002  0.008 |  0.408  0.060  0.119 |
## 349.1768_0.501                |  0.080  0.001  0.006 | -0.051  0.001  0.003 |
## 376.3184_0.456                | -0.342  0.016  0.056 |  0.321  0.037  0.050 |
## 572.3994_0.467                | -0.137  0.003  0.029 |  0.076  0.002  0.009 |
## 188.0705_0.471                | -0.056  0.000  0.002 |  0.418  0.063  0.102 |
##                                Dim.3    ctr   cos2  
## 553.3887_0.421                -0.379  0.084  0.089 |
## 666.637_0.431                 -0.075  0.003  0.006 |
## 489.2284_0.496                 0.694  0.281  0.085 |
## 696.649_0.442                 -0.366  0.078  0.069 |
## 297.1938_0.496                 0.301  0.053  0.027 |
## 356.2792_0.463                 0.115  0.008  0.010 |
## 349.1768_0.501                 0.095  0.005  0.009 |
## 376.3184_0.456                -0.121  0.009  0.007 |
## 572.3994_0.467                 0.229  0.031  0.081 |
## 188.0705_0.471                -0.034  0.001  0.001 |
## 
## Supplementary categories (the 10 first)
##                                    Dist     Dim.1    cos2  v.test     Dim.2
## sample_ID_6101_U1_HILICPOS_59 |  36.227 | -21.292   0.345  -0.783 |  -4.578
## sample_ID_6101_U2_HILICPOS_30 |  37.795 | -16.899   0.200  -0.621 |  -4.135
## sample_ID_6101_U3_HILICPOS_29 |  33.252 | -19.751   0.353  -0.726 |  -8.982
## sample_ID_6101_U4_HILICPOS_14 |  35.958 | -20.050   0.311  -0.737 |  -4.743
## sample_ID_6102_U1_HILICPOS_26 |  32.026 | -19.203   0.360  -0.706 |  -4.873
## sample_ID_6102_U2_HILICPOS_16 |  34.164 | -17.005   0.248  -0.625 |  -6.209
## sample_ID_6102_U3_HILICPOS_48 |  40.272 | -16.865   0.175  -0.620 |  -1.346
## sample_ID_6102_U4_HILICPOS_50 |  33.636 | -17.520   0.271  -0.644 |  -3.408
## sample_ID_6103_U1_HILICPOS_21 |  41.195 | -11.622   0.080  -0.427 |  -7.221
## sample_ID_6103_U2_HILICPOS_60 |  38.193 | -13.993   0.134  -0.514 |  -5.251
##                                  cos2  v.test     Dim.3    cos2  v.test  
## sample_ID_6101_U1_HILICPOS_59   0.016  -0.275 |   4.134   0.013   0.316 |
## sample_ID_6101_U2_HILICPOS_30   0.012  -0.248 |   2.666   0.005   0.204 |
## sample_ID_6101_U3_HILICPOS_29   0.073  -0.539 |   3.135   0.009   0.240 |
## sample_ID_6101_U4_HILICPOS_14   0.017  -0.285 |  -1.171   0.001  -0.090 |
## sample_ID_6102_U1_HILICPOS_26   0.023  -0.292 |  -2.010   0.004  -0.154 |
## sample_ID_6102_U2_HILICPOS_16   0.033  -0.373 |  -1.406   0.002  -0.108 |
## sample_ID_6102_U3_HILICPOS_48   0.001  -0.081 |   1.705   0.002   0.130 |
## sample_ID_6102_U4_HILICPOS_50   0.010  -0.204 |  -4.219   0.016  -0.323 |
## sample_ID_6103_U1_HILICPOS_21   0.031  -0.433 |  -1.279   0.001  -0.098 |
## sample_ID_6103_U2_HILICPOS_60   0.019  -0.315 |   2.224   0.003   0.170 |
Dist Dim.1 cos2 v.test Dim.2 cos2 v.test Dim.3 cos2 v.test
sample_ID_6101_U1_HILICPOS_59 | 36.227 | -21.292 0.345 -0.783 | -4.578 0.016 -0.275 | 4.134 0.013 0.316 |
sample_ID_6101_U2_HILICPOS_30 | 37.795 | -16.899 0.200 -0.621 | -4.135 0.012 -0.248 | 2.666 0.005 0.204 |
sample_ID_6101_U3_HILICPOS_29 | 33.252 | -19.751 0.353 -0.726 | -8.982 0.073 -0.539 | 3.135 0.009 0.240 |
sample_ID_6101_U4_HILICPOS_14 | 35.958 | -20.050 0.311 -0.737 | -4.743 0.017 -0.285 | -1.171 0.001 -0.090 |
sample_ID_6102_U1_HILICPOS_26 | 32.026 | -19.203 0.360 -0.706 | -4.873 0.023 -0.292 | -2.010 0.004 -0.154 |
sample_ID_6102_U2_HILICPOS_16 | 34.164 | -17.005 0.248 -0.625 | -6.209 0.033 -0.373 | -1.406 0.002 -0.108 |
sample_ID_6102_U3_HILICPOS_48 | 40.272 | -16.865 0.175 -0.620 | -1.346 0.001 -0.081 | 1.705 0.002 0.130 |
sample_ID_6102_U4_HILICPOS_50 | 33.636 | -17.520 0.271 -0.644 | -3.408 0.010 -0.204 | -4.219 0.016 -0.323 |
sample_ID_6103_U1_HILICPOS_21 | 41.195 | -11.622 0.080 -0.427 | -7.221 0.031 -0.433 | -1.279 0.001 -0.098 |
sample_ID_6103_U2_HILICPOS_60 | 38.193 | -13.993 0.134 -0.514 | -5.251 0.019 -0.315 | 2.224 0.003 0.170 |
# pull PC coordinates into df
PC_coord_QC_log2 <- as.data.frame(PCA.DC_imp_metabind_clust_log2$ind$coord)

# bind back metadata from cols 1-10
PC_coord_QC_log2 <- bind_cols(DC_imp_metabind_clust_log2[,1:11], PC_coord_QC_log2)

# grab some variance explained
importance_QC <- PCA.DC_imp_metabind_clust_log2$eig

# set variance explained with PC1, round to 2 digits
PC1_withQC <- round(importance_QC[1,2], 2)

# set variance explained with PC2, round to 2 digits
PC2_withQC <- round(importance_QC[2,2], 2)

Plots

Using FactoExtra package

# scree plot
fviz_eig(PCA.DC_imp_metabind_clust_log2)

# get eigenvalues
kable(get_eig(PCA.DC_imp_metabind_clust_log2))
eigenvalue variance.percent cumulative.variance.percent
Dim.1 740.3189051 34.7237156 34.72372
Dim.2 277.7684601 13.0283760 47.75209
Dim.3 170.9426797 8.0178487 55.76994
Dim.4 111.5554061 5.2323643 61.00230
Dim.5 103.7152804 4.8646332 65.86694
Dim.6 76.3952643 3.5832226 69.45016
Dim.7 71.3504685 3.3466029 72.79676
Dim.8 54.3616737 2.5497651 75.34653
Dim.9 46.7113400 2.1909359 77.53746
Dim.10 43.2590916 2.0290126 79.56648
Dim.11 42.9356262 2.0138409 81.58032
Dim.12 34.0186296 1.5956005 83.17592
Dim.13 32.8249413 1.5396121 84.71553
Dim.14 24.3537740 1.1422828 85.85781
Dim.15 23.2861743 1.0922084 86.95002
Dim.16 19.9375981 0.9351477 87.88517
Dim.17 19.3408314 0.9071571 88.79233
Dim.18 17.0738063 0.8008251 89.59315
Dim.19 15.8305812 0.7425133 90.33566
Dim.20 13.8599354 0.6500826 90.98575
Dim.21 13.3903408 0.6280569 91.61380
Dim.22 13.0609707 0.6126082 92.22641
Dim.23 12.0090727 0.5632703 92.78968
Dim.24 11.8106898 0.5539654 93.34365
Dim.25 10.4636200 0.4907828 93.83443
Dim.26 9.8390777 0.4614894 94.29592
Dim.27 8.9053376 0.4176935 94.71361
Dim.28 8.6193822 0.4042811 95.11789
Dim.29 7.9110564 0.3710580 95.48895
Dim.30 7.5854166 0.3557843 95.84474
Dim.31 7.0065218 0.3286320 96.17337
Dim.32 6.8569526 0.3216166 96.49499
Dim.33 6.5478600 0.3071190 96.80210
Dim.34 5.9481472 0.2789903 97.08110
Dim.35 5.6956475 0.2671471 97.34824
Dim.36 5.5550901 0.2605544 97.60880
Dim.37 5.4044490 0.2534888 97.86229
Dim.38 5.0799936 0.2382706 98.10056
Dim.39 4.8911355 0.2294125 98.32997
Dim.40 4.6293744 0.2171349 98.54710
Dim.41 4.6142178 0.2164240 98.76353
Dim.42 4.3408372 0.2036014 98.96713
Dim.43 3.9332019 0.1844818 99.15161
Dim.44 3.7895494 0.1777440 99.32935
Dim.45 3.4627969 0.1624181 99.49177
Dim.46 3.3282705 0.1561083 99.64788
Dim.47 2.9981728 0.1406255 99.78851
Dim.48 2.6529236 0.1244320 99.91294
Dim.49 0.4254977 0.0199574 99.93290
Dim.50 0.4073314 0.0191054 99.95200
Dim.51 0.3413914 0.0160125 99.96801
Dim.52 0.3201818 0.0150177 99.98303
Dim.53 0.2590048 0.0121483 99.99518
Dim.54 0.1027576 0.0048197 100.00000
Dim.55 0.0000092 0.0000004 100.00000
# scores plot
fviz_pca_ind(PCA.DC_imp_metabind_clust_log2)

Manual scores plots

# manual scores plot
(PCA_withQCs <- PC_coord_QC_log2 %>%
  ggplot(aes(x = Dim.1, y = Dim.2,
             fill = factor(Intervention, levels = c("Yellow", "Red", "QC")))) +
  geom_point(shape = 21, alpha = 0.8) +
  scale_fill_manual(values = c("gold", "tomato1", "light grey")) +
  scale_color_manual(values = "black") +  
  theme_minimal() +
  coord_fixed(PC2_withQC/PC1_withQC) +
  labs(x = glue::glue("PC1: {PC1_withQC}%"),
       y = glue::glue("PC2: {PC2_withQC}%"),
       fill = "Group",
       title = "Principal Components Analysis Scores Plot",
       subtitle = "Log2 transformed data"))

Without QCs

Wrangle

DC_imp_metabind_clust_log2_noQCs <- DC_imp_metabind_clust_log2 %>%
  filter(Intervention != "QC")

PCA.DC_imp_metabind_clust_log2_noQCs <- PCA(DC_imp_metabind_clust_log2_noQCs, # wide data
                               quali.sup=1:11, # remove qualitative variables
                               graph=FALSE, # don't graph
                               scale.unit=FALSE) # don't scale, we already did this

# look at summary
kable(summary(PCA.DC_imp_metabind_clust_log2_noQCs))
## 
## Call:
## PCA(X = DC_imp_metabind_clust_log2_noQCs, scale.unit = FALSE,  
##      quali.sup = 1:11, graph = FALSE) 
## 
## 
## Eigenvalues
##                        Dim.1   Dim.2   Dim.3   Dim.4   Dim.5   Dim.6   Dim.7
## Variance             585.594 249.121 131.532 129.672  89.127  84.013  65.178
## % of var.             29.946  12.739   6.726   6.631   4.558   4.296   3.333
## Cumulative % of var.  29.946  42.685  49.412  56.043  60.600  64.897  68.230
##                        Dim.8   Dim.9  Dim.10  Dim.11  Dim.12  Dim.13  Dim.14
## Variance              54.491  50.986  50.334  42.612  38.301  28.482  27.486
## % of var.              2.787   2.607   2.574   2.179   1.959   1.456   1.406
## Cumulative % of var.  71.016  73.623  76.197  78.377  80.335  81.792  83.197
##                       Dim.15  Dim.16  Dim.17  Dim.18  Dim.19  Dim.20  Dim.21
## Variance              24.389  22.813  21.557  19.017  16.417  15.714  15.582
## % of var.              1.247   1.167   1.102   0.972   0.840   0.804   0.797
## Cumulative % of var.  84.444  85.611  86.713  87.686  88.525  89.329  90.126
##                       Dim.22  Dim.23  Dim.24  Dim.25  Dim.26  Dim.27  Dim.28
## Variance              14.288  13.802  12.354  11.492  10.685  10.090   9.402
## % of var.              0.731   0.706   0.632   0.588   0.546   0.516   0.481
## Cumulative % of var.  90.857  91.562  92.194  92.782  93.328  93.844  94.325
##                       Dim.29  Dim.30  Dim.31  Dim.32  Dim.33  Dim.34  Dim.35
## Variance               9.011   8.173   8.044   7.654   6.944   6.716   6.637
## % of var.              0.461   0.418   0.411   0.391   0.355   0.343   0.339
## Cumulative % of var.  94.786  95.204  95.615  96.006  96.361  96.705  97.044
##                       Dim.36  Dim.37  Dim.38  Dim.39  Dim.40  Dim.41  Dim.42
## Variance               6.400   5.937   5.704   5.658   5.398   5.112   4.629
## % of var.              0.327   0.304   0.292   0.289   0.276   0.261   0.237
## Cumulative % of var.  97.372  97.675  97.967  98.256  98.532  98.794  99.030
##                       Dim.43  Dim.44  Dim.45  Dim.46  Dim.47
## Variance               4.424   4.038   3.882   3.514   3.103
## % of var.              0.226   0.206   0.199   0.180   0.159
## Cumulative % of var.  99.257  99.463  99.662  99.841 100.000
## 
## Individuals (the 10 first)
##                          Dist     Dim.1     ctr    cos2     Dim.2     ctr
## 1                   |  32.524 | -13.061   0.607   0.161 |  -8.055   0.543
## 2                   |  27.856 | -10.027   0.358   0.130 |  -3.802   0.121
## 3                   |  39.443 |  -2.753   0.027   0.005 |  -5.020   0.211
## 4                   |  32.636 | -13.645   0.662   0.175 |  -6.195   0.321
## 5                   |  34.700 | -11.193   0.446   0.104 |  -2.435   0.050
## 6                   |  76.822 |  70.444  17.654   0.841 |  -3.040   0.077
## 7                   |  39.426 |  -9.359   0.312   0.056 |  -2.057   0.035
## 8                   |  35.511 |  -2.385   0.020   0.005 |  -2.629   0.058
## 9                   |  31.664 |  -5.558   0.110   0.031 |  -9.805   0.804
## 10                  |  36.701 |  -5.651   0.114   0.024 |   2.991   0.075
##                        cos2     Dim.3     ctr    cos2  
## 1                     0.061 |  -7.037   0.784   0.047 |
## 2                     0.019 |  -8.158   1.054   0.086 |
## 3                     0.016 |   0.528   0.004   0.000 |
## 4                     0.036 |  -9.149   1.326   0.079 |
## 5                     0.005 |  -7.381   0.863   0.045 |
## 6                     0.002 |  -8.994   1.281   0.014 |
## 7                     0.003 |  -3.208   0.163   0.007 |
## 8                     0.005 |  -0.141   0.000   0.000 |
## 9                     0.096 |   2.393   0.091   0.006 |
## 10                    0.007 |  -8.613   1.175   0.055 |
## 
## Variables (the 10 first)
##                        Dim.1    ctr   cos2    Dim.2    ctr   cos2    Dim.3
## 553.3887_0.421      |  0.004  0.000  0.000 |  0.626  0.157  0.224 |  0.052
## 666.637_0.431       |  0.103  0.002  0.011 |  0.090  0.003  0.008 | -0.340
## 489.2284_0.496      | -0.231  0.009  0.040 |  0.496  0.099  0.184 |  0.174
## 696.649_0.442       |  0.047  0.000  0.001 |  0.504  0.102  0.112 | -0.153
## 297.1938_0.496      |  1.185  0.240  0.836 | -0.109  0.005  0.007 | -0.038
## 356.2792_0.463      | -0.440  0.033  0.126 |  0.096  0.004  0.006 | -0.098
## 349.1768_0.501      |  0.028  0.000  0.001 | -0.142  0.008  0.018 | -0.057
## 376.3184_0.456      | -0.463  0.037  0.089 |  0.234  0.022  0.023 | -0.373
## 572.3994_0.467      | -0.236  0.010  0.074 | -0.158  0.010  0.033 |  0.100
## 188.0705_0.471      | -0.317  0.017  0.052 |  0.267  0.029  0.037 | -0.101
##                        ctr   cos2  
## 553.3887_0.421       0.002  0.002 |
## 666.637_0.431        0.088  0.115 |
## 489.2284_0.496       0.023  0.023 |
## 696.649_0.442        0.018  0.010 |
## 297.1938_0.496       0.001  0.001 |
## 356.2792_0.463       0.007  0.006 |
## 349.1768_0.501       0.002  0.003 |
## 376.3184_0.456       0.106  0.058 |
## 572.3994_0.467       0.008  0.013 |
## 188.0705_0.471       0.008  0.005 |
## 
## Supplementary categories (the 10 first)
##                          Dist     Dim.1    cos2  v.test     Dim.2    cos2
## 6101_U1_HILICPOS_59 |  32.524 | -13.061   0.161  -0.540 |  -8.055   0.061
## 6101_U2_HILICPOS_30 |  35.207 |  -9.193   0.068  -0.380 |  -6.015   0.029
## 6101_U3_HILICPOS_29 |  28.936 |  -9.532   0.109  -0.394 |  -9.966   0.119
## 6101_U4_HILICPOS_14 |  32.217 | -11.100   0.119  -0.459 |  -4.683   0.021
## 6102_U1_HILICPOS_26 |  27.856 | -10.027   0.130  -0.414 |  -3.802   0.019
## 6102_U2_HILICPOS_16 |  30.721 |  -7.607   0.061  -0.314 |  -4.640   0.023
## 6102_U3_HILICPOS_48 |  38.090 | -10.278   0.073  -0.425 |  -3.604   0.009
## 6102_U4_HILICPOS_50 |  30.369 |  -9.303   0.094  -0.384 |  -1.919   0.004
## 6103_U1_HILICPOS_21 |  39.443 |  -2.753   0.005  -0.114 |  -5.020   0.016
## 6103_U2_HILICPOS_60 |  36.118 |  -6.110   0.029  -0.253 |  -6.046   0.028
##                      v.test     Dim.3    cos2  v.test  
## 6101_U1_HILICPOS_59  -0.510 |  -7.037   0.047  -0.614 |
## 6101_U2_HILICPOS_30  -0.381 |  -4.210   0.014  -0.367 |
## 6101_U3_HILICPOS_29  -0.631 |  -1.908   0.004  -0.166 |
## 6101_U4_HILICPOS_14  -0.297 | -11.678   0.131  -1.018 |
## 6102_U1_HILICPOS_26  -0.241 |  -8.158   0.086  -0.711 |
## 6102_U2_HILICPOS_16  -0.294 |  -2.585   0.007  -0.225 |
## 6102_U3_HILICPOS_48  -0.228 |  -7.626   0.040  -0.665 |
## 6102_U4_HILICPOS_50  -0.122 | -16.349   0.290  -1.426 |
## 6103_U1_HILICPOS_21  -0.318 |   0.528   0.000   0.046 |
## 6103_U2_HILICPOS_60  -0.383 |   5.178   0.021   0.451 |
Dist Dim.1 cos2 v.test Dim.2 cos2 v.test Dim.3 cos2 v.test
6101_U1_HILICPOS_59 | 32.524 | -13.061 0.161 -0.540 | -8.055 0.061 -0.510 | -7.037 0.047 -0.614 |
6101_U2_HILICPOS_30 | 35.207 | -9.193 0.068 -0.380 | -6.015 0.029 -0.381 | -4.210 0.014 -0.367 |
6101_U3_HILICPOS_29 | 28.936 | -9.532 0.109 -0.394 | -9.966 0.119 -0.631 | -1.908 0.004 -0.166 |
6101_U4_HILICPOS_14 | 32.217 | -11.100 0.119 -0.459 | -4.683 0.021 -0.297 | -11.678 0.131 -1.018 |
6102_U1_HILICPOS_26 | 27.856 | -10.027 0.130 -0.414 | -3.802 0.019 -0.241 | -8.158 0.086 -0.711 |
6102_U2_HILICPOS_16 | 30.721 | -7.607 0.061 -0.314 | -4.640 0.023 -0.294 | -2.585 0.007 -0.225 |
6102_U3_HILICPOS_48 | 38.090 | -10.278 0.073 -0.425 | -3.604 0.009 -0.228 | -7.626 0.040 -0.665 |
6102_U4_HILICPOS_50 | 30.369 | -9.303 0.094 -0.384 | -1.919 0.004 -0.122 | -16.349 0.290 -1.426 |
6103_U1_HILICPOS_21 | 39.443 | -2.753 0.005 -0.114 | -5.020 0.016 -0.318 | 0.528 0.000 0.046 |
6103_U2_HILICPOS_60 | 36.118 | -6.110 0.029 -0.253 | -6.046 0.028 -0.383 | 5.178 0.021 0.451 |
# pull PC coordinates into df
PC_coord_noQCs_log2 <- as.data.frame(PCA.DC_imp_metabind_clust_log2_noQCs$ind$coord)

# bind back metadata from cols 1-10
PC_coord_noQCs_log2 <- bind_cols(DC_imp_metabind_clust_log2_noQCs[,1:11], PC_coord_noQCs_log2)

# grab some variance explained
importance_noQC <- PCA.DC_imp_metabind_clust_log2_noQCs$eig

# set variance explained with PC1, round to 2 digits
PC1_noQC <- round(importance_noQC[1,2], 2)

# set variance explained with PC2, round to 2 digits
PC2_noQC <- round(importance_noQC[2,2], 2)

Plots

Using FactoExtra

# scree plot
fviz_eig(PCA.DC_imp_metabind_clust_log2_noQCs)

# scores plot
fviz_pca_ind(PCA.DC_imp_metabind_clust_log2_noQCs)

# plot of contributions from features to PC1
(var_contrib_noQCs_PC1 <- fviz_contrib(PCA.DC_imp_metabind_clust_log2_noQCs,
             choice = "var",
             axes = 1,
             top = 25,
             title = "Var contribution to PC1: no QCs"))

# plot of contributions from features to PC2
(var_contrib_noQCs_PC2 <- fviz_contrib(PCA.DC_imp_metabind_clust_log2_noQCs,
             choice = "var",
             axes = 2,
             top = 25,
             title = "Var contribution to PC2: no QCs"))

Manual scores plots

Yellow vs red

(PCA_withoutQCs <- PC_coord_noQCs_log2 %>%
  ggplot(aes(x = Dim.1, 
             y = Dim.2,
             fill = Intervention)) +
    geom_point(shape = 21, alpha = 0.8) +
    geom_hline(yintercept = 0, linetype = "dashed", alpha=0.5) +
    geom_vline(xintercept = 0, linetype = "dashed", alpha=0.5) +
    scale_fill_manual(values = c("gold", "tomato1")) +
    scale_color_manual(values = "black") +  
    theme_minimal() +
    coord_fixed(PC2_noQC/PC1_noQC) +
    labs(x = glue::glue("PC1: {PC1_noQC}%"),
         y = glue::glue("PC2: {PC2_noQC}%"),
         fill = "Intervention",
         title = "Principal Components Analysis Scores Plot",
         subtitle = "Log2 transformed data, without QCs"))

ggplotly(PCA_withoutQCs)

pre vs post

(PCA_withoutQCs.pre_post <- PC_coord_noQCs_log2 %>%
  ggplot(aes(x = Dim.1, 
             y = Dim.2,
             fill = factor(pre_post_intervention, levels = c("pre_Yellow",
                                                             "post_Yellow",
                                                             "pre_Red",
                                                             "post_Red")),
             text = sample_ID)) +
    geom_point(shape = 21, alpha = 0.8) +
    geom_hline(yintercept = 0, linetype = "dashed", alpha=0.5) +
    geom_vline(xintercept = 0, linetype = "dashed", alpha=0.5) +
    scale_fill_manual(values = c("gray", "yellow1", "pink1", "red2")) +
    scale_color_manual(values = "black") +  
    theme_minimal() +
    coord_fixed(PC2_noQC/PC1_noQC) +
    labs(x = glue::glue("PC1: {PC1_noQC}%"),
         y = glue::glue("PC2: {PC2_noQC}%"),
         fill = "pre_post",
         title = "Principal Components Analysis Scores Plot",
         subtitle = "Log2 transformed, without QCs"))

ggplotly(PCA_withoutQCs.pre_post,
         tooltip = "text") 

Notes

Looks like subject 6106 and 6112 are outliers

Removal of outlier

With QCs

Wrangle

# go back to wide data
DC_imp_nooutliers_log2 <- DC_imp_metabind_clust_log2 %>%
  filter(Subject != 6106,
         Subject != 6112)

PCA.DC_imp_nooutliers_log2 <- PCA(DC_imp_nooutliers_log2,  # wide data
                                   quali.sup = 1:11, # remove qualitative variables
                                   graph = FALSE, # don't graph
                                   scale.unit = FALSE) # don't scale, already transformed data

# PCA summary
summary(PCA.DC_imp_nooutliers_log2)
## 
## Call:
## PCA(X = DC_imp_nooutliers_log2, scale.unit = FALSE, quali.sup = 1:11,  
##      graph = FALSE) 
## 
## 
## Eigenvalues
##                        Dim.1   Dim.2   Dim.3   Dim.4   Dim.5   Dim.6   Dim.7
## Variance             627.901 132.609 130.401  87.427  84.085  63.521  54.604
## % of var.             37.554   7.931   7.799   5.229   5.029   3.799   3.266
## Cumulative % of var.  37.554  45.485  53.284  58.513  63.542  67.341  70.607
##                        Dim.8   Dim.9  Dim.10  Dim.11  Dim.12  Dim.13  Dim.14
## Variance              49.255  46.791  40.151  36.198  27.295  26.088  22.043
## % of var.              2.946   2.799   2.401   2.165   1.633   1.560   1.318
## Cumulative % of var.  73.553  76.352  78.753  80.918  82.550  84.111  85.429
##                       Dim.15  Dim.16  Dim.17  Dim.18  Dim.19  Dim.20  Dim.21
## Variance              20.917  19.266  16.256  15.705  14.491  13.294  13.016
## % of var.              1.251   1.152   0.972   0.939   0.867   0.795   0.778
## Cumulative % of var.  86.680  87.832  88.805  89.744  90.611  91.406  92.184
##                       Dim.22  Dim.23  Dim.24  Dim.25  Dim.26  Dim.27  Dim.28
## Variance              11.730  10.631  10.158   9.760   9.024   7.765   7.199
## % of var.              0.702   0.636   0.608   0.584   0.540   0.464   0.431
## Cumulative % of var.  92.886  93.522  94.129  94.713  95.252  95.717  96.147
##                       Dim.29  Dim.30  Dim.31  Dim.32  Dim.33  Dim.34  Dim.35
## Variance               6.943   6.633   6.121   5.994   5.659   5.457   4.898
## % of var.              0.415   0.397   0.366   0.358   0.338   0.326   0.293
## Cumulative % of var.  96.563  96.959  97.326  97.684  98.022  98.349  98.642
##                       Dim.36  Dim.37  Dim.38  Dim.39  Dim.40  Dim.41  Dim.42
## Variance               4.779   4.227   4.101   3.989   3.394   0.521   0.486
## % of var.              0.286   0.253   0.245   0.239   0.203   0.031   0.029
## Cumulative % of var.  98.928  99.180  99.426  99.664  99.867  99.898  99.927
##                       Dim.43  Dim.44  Dim.45  Dim.46  Dim.47
## Variance               0.404   0.379   0.306   0.123   0.000
## % of var.              0.024   0.023   0.018   0.007   0.000
## Cumulative % of var.  99.952  99.974  99.993 100.000 100.000
## 
## Individuals (the 10 first)
##                                    Dist     Dim.1     ctr    cos2     Dim.2
## 1                             |  33.465 | -17.093   0.969   0.261 |   6.208
## 2                             |  29.904 | -16.159   0.866   0.292 |   7.362
## 3                             |  40.231 |  -9.040   0.271   0.050 | -10.689
## 4                             |  33.619 | -16.923   0.950   0.253 |   7.666
## 5                             |  36.076 | -15.402   0.787   0.182 |   7.812
## 6                             |  40.824 | -14.446   0.692   0.125 |  11.070
## 7                             |  37.101 | -10.584   0.372   0.081 |  -3.948
## 8                             |  31.819 | -10.238   0.348   0.104 |  -7.422
## 9                             |  37.589 |  -8.657   0.249   0.053 |   9.439
## 10                            |  33.269 | -11.561   0.443   0.121 |  -2.054
##                                   ctr    cos2     Dim.3     ctr    cos2  
## 1                               0.605   0.034 |  -5.882   0.553   0.031 |
## 2                               0.851   0.061 |  -1.130   0.020   0.001 |
## 3                               1.795   0.071 |  19.667   6.179   0.239 |
## 4                               0.923   0.052 |  -1.064   0.018   0.001 |
## 5                               0.959   0.047 |  -2.751   0.121   0.006 |
## 6                               1.925   0.074 | -17.441   4.860   0.183 |
## 7                               0.245   0.011 |  10.093   1.628   0.074 |
## 8                               0.865   0.054 |   4.872   0.379   0.023 |
## 9                               1.400   0.063 |   5.211   0.434   0.019 |
## 10                              0.066   0.004 |   1.279   0.026   0.001 |
## 
## Variables (the 10 first)
##                                  Dim.1    ctr   cos2    Dim.2    ctr   cos2  
## 553.3887_0.421                |  0.418  0.028  0.137 | -0.019  0.000  0.000 |
## 666.637_0.431                 |  0.181  0.005  0.039 |  0.280  0.059  0.093 |
## 489.2284_0.496                |  2.285  0.832  0.895 |  0.144  0.016  0.004 |
## 696.649_0.442                 |  0.049  0.000  0.002 |  0.194  0.028  0.025 |
## 297.1938_0.496                |  1.569  0.392  0.899 |  0.041  0.001  0.001 |
## 356.2792_0.463                |  0.260  0.011  0.053 | -0.091  0.006  0.006 |
## 349.1768_0.501                |  0.123  0.002  0.014 | -0.194  0.028  0.035 |
## 376.3184_0.456                | -0.147  0.003  0.011 |  0.473  0.168  0.114 |
## 572.3994_0.467                |  0.011  0.000  0.000 | -0.177  0.024  0.047 |
## 188.0705_0.471                |  0.219  0.008  0.027 |  0.040  0.001  0.001 |
##                                Dim.3    ctr   cos2  
## 553.3887_0.421                 0.424  0.138  0.141 |
## 666.637_0.431                  0.131  0.013  0.020 |
## 489.2284_0.496                 0.015  0.000  0.000 |
## 696.649_0.442                  0.012  0.000  0.000 |
## 297.1938_0.496                 0.050  0.002  0.001 |
## 356.2792_0.463                 0.576  0.255  0.260 |
## 349.1768_0.501                 0.476  0.174  0.214 |
## 376.3184_0.456                 0.030  0.001  0.000 |
## 572.3994_0.467                 0.061  0.003  0.006 |
## 188.0705_0.471                 0.386  0.114  0.084 |
## 
## Supplementary categories (the 10 first)
##                                    Dist     Dim.1    cos2  v.test     Dim.2
## sample_ID_6101_U1_HILICPOS_59 |  33.465 | -17.093   0.261  -0.682 |   6.208
## sample_ID_6101_U2_HILICPOS_30 |  35.788 | -12.844   0.129  -0.513 |   3.822
## sample_ID_6101_U3_HILICPOS_29 |  30.728 | -17.283   0.316  -0.690 |  -1.690
## sample_ID_6101_U4_HILICPOS_14 |  33.786 | -16.688   0.244  -0.666 |   9.983
## sample_ID_6102_U1_HILICPOS_26 |  29.904 | -16.159   0.292  -0.645 |   7.362
## sample_ID_6102_U2_HILICPOS_16 |  32.408 | -14.302   0.195  -0.571 |   0.731
## sample_ID_6102_U3_HILICPOS_48 |  38.451 | -12.056   0.098  -0.481 |   7.878
## sample_ID_6102_U4_HILICPOS_50 |  31.614 | -13.930   0.194  -0.556 |  13.105
## sample_ID_6103_U1_HILICPOS_21 |  40.231 |  -9.040   0.050  -0.361 | -10.689
## sample_ID_6103_U2_HILICPOS_60 |  36.613 | -10.077   0.076  -0.402 | -12.601
##                                  cos2  v.test     Dim.3    cos2  v.test  
## sample_ID_6101_U1_HILICPOS_59   0.034   0.539 |  -5.882   0.031  -0.515 |
## sample_ID_6101_U2_HILICPOS_30   0.011   0.332 |  -2.178   0.004  -0.191 |
## sample_ID_6101_U3_HILICPOS_29   0.003  -0.147 |  -2.126   0.005  -0.186 |
## sample_ID_6101_U4_HILICPOS_14   0.087   0.867 |   0.370   0.000   0.032 |
## sample_ID_6102_U1_HILICPOS_26   0.061   0.639 |  -1.130   0.001  -0.099 |
## sample_ID_6102_U2_HILICPOS_16   0.001   0.063 |   1.974   0.004   0.173 |
## sample_ID_6102_U3_HILICPOS_48   0.042   0.684 |  -2.671   0.005  -0.234 |
## sample_ID_6102_U4_HILICPOS_50   0.172   1.138 |   8.632   0.075   0.756 |
## sample_ID_6103_U1_HILICPOS_21   0.071  -0.928 |  19.667   0.239   1.722 |
## sample_ID_6103_U2_HILICPOS_60   0.118  -1.094 |  13.110   0.128   1.148 |
# pull PC coordinates into df
PC_nooutliers_QC_log2 <- as.data.frame(PCA.DC_imp_nooutliers_log2$ind$coord)

# bind back metadata from cols 1-11
PC_nooutliers_QC_log2 <- bind_cols(DC_imp_nooutliers_log2[,1:11], PC_nooutliers_QC_log2)

# grab some variance explained
importance_nooutliers_QC <- PCA.DC_imp_nooutliers_log2$eig

# set variance explained with PC1, round to 2 digits
PC1_nooutliers_withQC <- round(importance_nooutliers_QC[1,2], 2)

# set variance explained with PC2, round to 2 digits
PC2_nooutliers_withQC <- round(importance_nooutliers_QC[2,2], 2)

Plots

Using FactoExtra package

# scree plot
fviz_eig(PCA.DC_imp_nooutliers_log2)

# scores plot
fviz_pca_ind(PCA.DC_imp_nooutliers_log2)

Manual scores plots

##### Red vs yellow

# manual scores plot
(PCA_nooutliers_withQCs <- PC_nooutliers_QC_log2 %>%
  ggplot(aes(x = Dim.1, y = Dim.2,
             fill = factor(Intervention, levels = c("Yellow", "Red", "QC")))) +
  geom_point(shape = 21, alpha = 0.8) +
  scale_fill_manual(values = c("gold", "tomato1", "light grey")) +
  scale_color_manual(values = "black") +  
  theme_minimal() +
  labs(x = glue::glue("PC1: {PC1_nooutliers_withQC}%"),
       y = glue::glue("PC2: {PC2_nooutliers_withQC}%"),
       fill = "Group",
       title = "Principal Components Analysis Scores Plot"))

Pre vs post
(PCA_nooutliers_prepost_withQCs <- PC_nooutliers_QC_log2 %>%
  ggplot(aes(x = Dim.1, 
             y = Dim.2,
             fill = factor(pre_post_intervention, levels = c("pre_Yellow",
                                                             "post_Yellow",
                                                             "pre_Red",
                                                             "post_Red")),
             text = sample_ID)) +
    geom_point(shape = 21, alpha = 0.8) +
    geom_hline(yintercept = 0, linetype = "dashed", alpha=0.5) +
    geom_vline(xintercept = 0, linetype = "dashed", alpha=0.5) +
    scale_fill_manual(values = c("gray", "yellow1", "pink1", "red2")) +
    scale_color_manual(values = "black") +  
    theme_minimal() +
    labs(x = glue::glue("PC1: {PC1_nooutliers_withQC}%"),
         y = glue::glue("PC2: {PC2_nooutliers_withQC}%"),
         fill = "pre_post",
         title = "Principal Components Analysis Scores Plot"))

ggplotly(PCA_nooutliers_prepost_withQCs,
         tooltip = "text") 

Without QCs

Wrangle

imp_nooutliers_noQCs_log2 <- DC_imp_nooutliers_log2 %>%
  filter(Intervention != "QC") 

PCA.imp_nooutliers_noQCs_log2 <- PCA(imp_nooutliers_noQCs_log2, # wide data
                               quali.sup=1:11, # remove qualitative variables
                               graph=FALSE, # don't graph
                               scale.unit=FALSE) # don't scale, we already did this

# look at summary
kable(summary(PCA.imp_nooutliers_noQCs_log2))
## 
## Call:
## PCA(X = imp_nooutliers_noQCs_log2, scale.unit = FALSE, quali.sup = 1:11,  
##      graph = FALSE) 
## 
## 
## Eigenvalues
##                        Dim.1   Dim.2   Dim.3   Dim.4   Dim.5   Dim.6   Dim.7
## Variance             162.991 156.695 108.060 100.946  76.717  66.115  60.723
## % of var.             12.878  12.380   8.538   7.976   6.061   5.224   4.798
## Cumulative % of var.  12.878  25.258  33.796  41.772  47.833  53.057  57.855
##                        Dim.8   Dim.9  Dim.10  Dim.11  Dim.12  Dim.13  Dim.14
## Variance              56.149  48.644  43.624  32.856  31.737  26.453  25.527
## % of var.              4.436   3.843   3.447   2.596   2.508   2.090   2.017
## Cumulative % of var.  62.291  66.135  69.581  72.177  74.685  76.775  78.792
##                       Dim.15  Dim.16  Dim.17  Dim.18  Dim.19  Dim.20  Dim.21
## Variance              24.429  19.506  18.859  18.098  16.052  15.765  14.078
## % of var.              1.930   1.541   1.490   1.430   1.268   1.246   1.112
## Cumulative % of var.  80.722  82.263  83.753  85.183  86.451  87.697  88.809
##                       Dim.22  Dim.23  Dim.24  Dim.25  Dim.26  Dim.27  Dim.28
## Variance              13.375  12.370  11.699  11.004   9.371   8.678   8.331
## % of var.              1.057   0.977   0.924   0.869   0.740   0.686   0.658
## Cumulative % of var.  89.866  90.843  91.768  92.637  93.377  94.063  94.721
##                       Dim.29  Dim.30  Dim.31  Dim.32  Dim.33  Dim.34  Dim.35
## Variance               8.001   7.452   7.301   6.802   6.686   5.874   5.755
## % of var.              0.632   0.589   0.577   0.537   0.528   0.464   0.455
## Cumulative % of var.  95.353  95.942  96.519  97.057  97.585  98.049  98.504
##                       Dim.36  Dim.37  Dim.38  Dim.39
## Variance               5.112   4.920   4.814   4.093
## % of var.              0.404   0.389   0.380   0.323
## Cumulative % of var.  98.908  99.296  99.677 100.000
## 
## Individuals (the 10 first)
##                          Dist     Dim.1     ctr    cos2     Dim.2     ctr
## 1                   |  29.639 |  -6.966   0.744   0.055 |  -6.785   0.735
## 2                   |  25.899 |  -8.549   1.121   0.109 |  -2.510   0.101
## 3                   |  39.188 |   6.069   0.565   0.024 |  21.551   7.410
## 4                   |  29.851 |  -9.218   1.303   0.095 |  -2.455   0.096
## 5                   |  33.064 |  -8.390   1.080   0.064 |  -4.228   0.285
## 6                   |  38.386 |  -7.741   0.919   0.041 | -19.359   5.980
## 7                   |  35.448 |   2.048   0.064   0.003 |  10.635   1.804
## 8                   |  30.089 |   5.832   0.522   0.038 |   6.456   0.665
## 9                   |  36.682 |  -9.735   1.454   0.070 |   2.854   0.130
## 10                  |  31.246 |   0.829   0.011   0.001 |   1.844   0.054
##                        cos2     Dim.3     ctr    cos2  
## 1                     0.052 | -13.669   4.322   0.213 |
## 2                     0.009 |  -5.842   0.790   0.051 |
## 3                     0.302 |   0.949   0.021   0.001 |
## 4                     0.007 | -14.586   4.922   0.239 |
## 5                     0.016 |  -3.618   0.303   0.012 |
## 6                     0.254 |   2.101   0.102   0.003 |
## 7                     0.090 |  17.020   6.702   0.231 |
## 8                     0.046 |   3.421   0.271   0.013 |
## 9                     0.006 |   4.527   0.474   0.015 |
## 10                    0.003 |  -6.698   1.038   0.046 |
## 
## Variables (the 10 first)
##                        Dim.1    ctr   cos2    Dim.2    ctr   cos2    Dim.3
## 553.3887_0.421      | -0.045  0.001  0.002 |  0.452  0.130  0.157 |  0.094
## 666.637_0.431       | -0.333  0.068  0.117 |  0.071  0.003  0.005 | -0.286
## 489.2284_0.496      |  0.042  0.001  0.003 | -0.048  0.001  0.003 | -0.056
## 696.649_0.442       | -0.228  0.032  0.029 | -0.032  0.001  0.001 | -0.222
## 297.1938_0.496      |  0.073  0.003  0.020 |  0.025  0.000  0.002 | -0.024
## 356.2792_0.463      | -0.001  0.000  0.000 |  0.631  0.254  0.272 |  0.172
## 349.1768_0.501      |  0.113  0.008  0.010 |  0.553  0.195  0.247 |  0.108
## 376.3184_0.456      | -0.533  0.174  0.121 | -0.081  0.004  0.003 | -0.095
## 572.3994_0.467      |  0.163  0.016  0.033 |  0.111  0.008  0.015 | -0.211
## 188.0705_0.471      | -0.086  0.005  0.004 |  0.394  0.099  0.075 |  0.288
##                        ctr   cos2  
## 553.3887_0.421       0.008  0.007 |
## 666.637_0.431        0.075  0.087 |
## 489.2284_0.496       0.003  0.005 |
## 696.649_0.442        0.046  0.028 |
## 297.1938_0.496       0.001  0.002 |
## 356.2792_0.463       0.027  0.020 |
## 349.1768_0.501       0.011  0.009 |
## 376.3184_0.456       0.008  0.004 |
## 572.3994_0.467       0.041  0.056 |
## 188.0705_0.471       0.077  0.040 |
## 
## Supplementary categories (the 10 first)
##                          Dist     Dim.1    cos2  v.test     Dim.2    cos2
## 6101_U1_HILICPOS_59 |  29.639 |  -6.966   0.055  -0.546 |  -6.785   0.052
## 6101_U2_HILICPOS_30 |  33.571 |  -4.271   0.016  -0.335 |  -2.824   0.007
## 6101_U3_HILICPOS_29 |  26.312 |  -0.158   0.000  -0.012 |  -1.345   0.003
## 6101_U4_HILICPOS_14 |  30.127 | -11.461   0.145  -0.898 |  -1.635   0.003
## 6102_U1_HILICPOS_26 |  25.899 |  -8.549   0.109  -0.670 |  -2.510   0.009
## 6102_U2_HILICPOS_16 |  29.349 |  -2.240   0.006  -0.175 |   1.917   0.004
## 6102_U3_HILICPOS_48 |  36.703 |  -8.146   0.049  -0.638 |  -4.207   0.013
## 6102_U4_HILICPOS_50 |  28.717 | -15.247   0.282  -1.194 |   5.541   0.037
## 6103_U1_HILICPOS_21 |  39.188 |   6.069   0.024   0.475 |  21.551   0.302
## 6103_U2_HILICPOS_60 |  35.133 |   9.055   0.066   0.709 |  15.644   0.198
##                      v.test     Dim.3    cos2  v.test  
## 6101_U1_HILICPOS_59  -0.542 | -13.669   0.213  -1.315 |
## 6101_U2_HILICPOS_30  -0.226 |  -4.982   0.022  -0.479 |
## 6101_U3_HILICPOS_29  -0.107 | -11.847   0.203  -1.140 |
## 6101_U4_HILICPOS_14  -0.131 |  -8.091   0.072  -0.778 |
## 6102_U1_HILICPOS_26  -0.201 |  -5.842   0.051  -0.562 |
## 6102_U2_HILICPOS_16   0.153 |   1.991   0.005   0.192 |
## 6102_U3_HILICPOS_48  -0.336 |  -9.108   0.062  -0.876 |
## 6102_U4_HILICPOS_50   0.443 |   1.738   0.004   0.167 |
## 6103_U1_HILICPOS_21   1.722 |   0.949   0.001   0.091 |
## 6103_U2_HILICPOS_60   1.250 |   2.204   0.004   0.212 |
Dist Dim.1 cos2 v.test Dim.2 cos2 v.test Dim.3 cos2 v.test
6101_U1_HILICPOS_59 | 29.639 | -6.966 0.055 -0.546 | -6.785 0.052 -0.542 | -13.669 0.213 -1.315 |
6101_U2_HILICPOS_30 | 33.571 | -4.271 0.016 -0.335 | -2.824 0.007 -0.226 | -4.982 0.022 -0.479 |
6101_U3_HILICPOS_29 | 26.312 | -0.158 0.000 -0.012 | -1.345 0.003 -0.107 | -11.847 0.203 -1.140 |
6101_U4_HILICPOS_14 | 30.127 | -11.461 0.145 -0.898 | -1.635 0.003 -0.131 | -8.091 0.072 -0.778 |
6102_U1_HILICPOS_26 | 25.899 | -8.549 0.109 -0.670 | -2.510 0.009 -0.201 | -5.842 0.051 -0.562 |
6102_U2_HILICPOS_16 | 29.349 | -2.240 0.006 -0.175 | 1.917 0.004 0.153 | 1.991 0.005 0.192 |
6102_U3_HILICPOS_48 | 36.703 | -8.146 0.049 -0.638 | -4.207 0.013 -0.336 | -9.108 0.062 -0.876 |
6102_U4_HILICPOS_50 | 28.717 | -15.247 0.282 -1.194 | 5.541 0.037 0.443 | 1.738 0.004 0.167 |
6103_U1_HILICPOS_21 | 39.188 | 6.069 0.024 0.475 | 21.551 0.302 1.722 | 0.949 0.001 0.091 |
6103_U2_HILICPOS_60 | 35.133 | 9.055 0.066 0.709 | 15.644 0.198 1.250 | 2.204 0.004 0.212 |
# pull PC coordinates into df
PC_coord_nooutliers_noQC_log2 <- as.data.frame(PCA.imp_nooutliers_noQCs_log2$ind$coord)

# bind back metadata from cols 1-11
PC_coord_nooutliers_noQC_log2 <- bind_cols(imp_nooutliers_noQCs_log2[,1:11], PC_coord_nooutliers_noQC_log2)

# grab some variance explained
importance_nooutliers_noQC <- PCA.imp_nooutliers_noQCs_log2$eig

# set variance explained with PC1, round to 2 digits
PC1_nooutliers_noQC <- round(importance_nooutliers_noQC[1,2], 2)

# set variance explained with PC2, round to 2 digits
PC2_nooutliers_noQC <- round(importance_nooutliers_noQC[2,2], 2)

Plots

Using FactoExtra

# scree plot
fviz_eig(PCA.imp_nooutliers_noQCs_log2)

# scores plot
fviz_pca_ind(PCA.imp_nooutliers_noQCs_log2)

# plot of contributions from features to PC1
(var_contrib_nooutliers_noQCs_PC1 <- fviz_contrib(PCA.imp_nooutliers_noQCs_log2,
             choice = "var",
             axes = 1,
             top = 20,
             title = "Var contribution to PC1: no outliers, no QCs"))

# plot of contributions from features to PC2
(var_contrib_nooutliers_noQCs_PC2 <- fviz_contrib(PCA.imp_nooutliers_noQCs_log2,
             choice = "var",
             axes = 2,
             top = 20,
             title = "Var contribution to PC2: no outliers, no QCs"))

Manual scores plots

Red vs yellow
(PCA_nooutliers_withoutQCs <- PC_coord_nooutliers_noQC_log2 %>%
  ggplot(aes(x = Dim.1, 
             y = Dim.2,
             fill = Intervention)) +
    geom_point(shape = 21, alpha = 0.8) +
    geom_hline(yintercept = 0, linetype = "dashed", alpha=0.5) +
    geom_vline(xintercept = 0, linetype = "dashed", alpha=0.5) +
    scale_fill_manual(values = c("tomato1", "gold")) +
    scale_color_manual(values = "black") +  
    theme_minimal() +
    coord_fixed(PC2_nooutliers_noQC/PC1_nooutliers_noQC) +
    labs(x = glue::glue("PC1: {PC1_nooutliers_noQC}%"),
         y = glue::glue("PC2: {PC2_nooutliers_noQC}%"),
         fill = "Intervention",
         title = "Principal Components Analysis Scores Plot"))

ggplotly(PCA_nooutliers_withoutQCs)
Pre vs post
(PCA_nooutliers_noQCs.prepost <- PC_coord_nooutliers_noQC_log2 %>%
  ggplot(aes(x = Dim.1, 
             y = Dim.2,
             fill = factor(pre_post_intervention, levels = c("pre_Yellow",
                                                             "post_Yellow",
                                                             "pre_Red",
                                                             "post_Red")),
             text = sample_ID)) +
    geom_point(shape = 21, alpha = 0.8) +
    geom_hline(yintercept = 0, linetype = "dashed", alpha=0.5) +
    geom_vline(xintercept = 0, linetype = "dashed", alpha=0.5) +
    scale_fill_manual(values = c("gray", "yellow1", "pink1", "red2")) +
    scale_color_manual(values = "black") +  
    theme_minimal() +
    coord_fixed(PC2_nooutliers_noQC/PC1_nooutliers_noQC) +
    labs(x = glue::glue("PC1: {PC1_nooutliers_noQC}%"),
         y = glue::glue("PC2: {PC2_nooutliers_noQC}%"),
         fill = "pre_post",
         title = "Principal Components Analysis Scores Plot",
         subtitle = "Log2 transformed data, no outliers"))

ggplotly(PCA_nooutliers_noQCs.prepost,
         tooltip = "text") 
M v F
(PCA_nooutliers_noQCs.MvsF <- PC_coord_nooutliers_noQC_log2 %>%
  ggplot(aes(x = Dim.1, 
             y = Dim.2,
             fill = factor(Sex, levels = c("M","F")),
             text = sample_ID)) +
    geom_point(shape = 21, alpha = 0.8) +
    geom_hline(yintercept = 0, linetype = "dashed", alpha=0.5) +
    geom_vline(xintercept = 0, linetype = "dashed", alpha=0.5) +
    scale_fill_manual(values = c("green", "pink")) +
    scale_color_manual(values = "black") +  
    theme_minimal() +
    coord_fixed(PC2_nooutliers_noQC/PC1_nooutliers_noQC) +
    labs(x = glue::glue("PC1: {PC1_nooutliers_noQC}%"),
         y = glue::glue("PC2: {PC2_nooutliers_noQC}%"),
         fill = "pre_post",
         title = "Principal Components Analysis Scores Plot",
         subtitle = "Log2 transformed data, no 6106"))

ggplotly(PCA_nooutliers_noQCs.MvsF,
         tooltip = "text")

PCAtools pckg

  if (!requireNamespace('BiocManager', quietly = TRUE))
    install.packages('BiocManager')

  BiocManager::install('PCAtools')
library(PCAtools)

W/ Outliers

Data wrangling

# create rel abund df suitable for PCAtools package

DC_imp_clust_omicsdata_outliers_forPCAtools <- as.data.frame(t(DC_imp_metabind_clust_log2[,-c(2:11)])) # transpose df 

names(DC_imp_clust_omicsdata_outliers_forPCAtools) <- DC_imp_clust_omicsdata_outliers_forPCAtools[1,] # make sample IDs column names

DC_imp_clust_omicsdata_outliers_forPCAtools <- DC_imp_clust_omicsdata_outliers_forPCAtools[-1,] # remove sample ID row

# create metadata df suitable for PCAtools pckg

metadata_outliers_forPCAtools <- metadata %>%
  column_to_rownames(var = "sample_ID") # change sample ID column to rownames

# create a vector so that col names in abundance df matches metadata df
order_outliers_forPCAtools <- match(rownames(metadata_outliers_forPCAtools), colnames(DC_imp_clust_omicsdata_outliers_forPCAtools))

# reorder col names in abundance df so that it matches metadata
abundata_outliers_reordered_forPCAtools <- DC_imp_clust_omicsdata_outliers_forPCAtools[ ,order_outliers_forPCAtools] 

# change abundance df to numeric
abundata_outliers_reordered_forPCAtools <- abundata_outliers_reordered_forPCAtools %>%
  mutate_all(as.numeric)

# Log transform
log2_abundata_outliers_forPCAtools <- log2(abundata_outliers_reordered_forPCAtools)


# unite pre_post column with intervention column to create pre_intervention column
metadata_outliers_forPCAtools <- metadata_outliers_forPCAtools %>%
  unite(col = "pre_post_intervention",
        c("pre_post","Intervention"),
        sep = "_",
        remove = FALSE)

PCA

# pca
p_outliers <- PCAtools::pca(log2_abundata_outliers_forPCAtools, 
         metadata = metadata_outliers_forPCAtools, 
         scale = FALSE # using scaled data already (log2 transformed)
         
)

# plot

PCAtools::biplot(p_outliers,
                 showLoadings = TRUE, # show variables that contribute the most to PCs
                 lab = NULL,
                 title = )

More PCAs

Pre vs post both

PC1vPC2
  biplot(p_outliers,
          lab = paste0(metadata_outliers_forPCAtools$Subject),
          colby = 'pre_post_intervention',
          colkey = c("pre_Yellow" = "yellow",
                     "post_Yellow" = "yellow4",
                     "pre_Red" = "red",
                     "post_Red" = "red4"),
         # ellipse config
         ellipse = TRUE,
         ellipseType = 't',
         ellipseLevel = 0.95,
         ellipseFill = TRUE,
         ellipseAlpha = 0.2,
         ellipseLineSize = 1.0,
         xlim = c(-25,25), ylim = c(-10, 10),
         hline = 0, vline = 0,
         legendPosition = 'right',
         title = "PCA Scores Plot with 95% Confidence Interval",
         subtitle = "Log2 transformed data, HILIC (+), with outliers, no QCs")

No outliers

Data wrangling

# create rel abund df suitable for PCAtools package

DC_imp_clust_omicsdata_forPCAtools <- as.data.frame(t(DC_imp_metabind_clust_log2[,-c(2:11)])) # transpose df 

names(DC_imp_clust_omicsdata_forPCAtools) <- DC_imp_clust_omicsdata_forPCAtools[1,] # make sample IDs column names

DC_imp_clust_omicsdata_forPCAtools <- DC_imp_clust_omicsdata_forPCAtools[-1,] # remove sample ID row

DC_imp_clust_omicsdata_forPCAtools <- DC_imp_clust_omicsdata_forPCAtools %>%
  dplyr::select(!contains("QC")) # remove QC observations


# create metadata df suitable for PCAtools pckg

metadata_forPCAtools <- metadata %>%
  column_to_rownames(var = "sample_ID") # change sample ID column to rownames

# create a vector so that col names in abundance df matches metadata df
order_forPCAtools <- match(rownames(metadata_forPCAtools), colnames(DC_imp_clust_omicsdata_forPCAtools))

# reorder col names in abundance df so that it matches metadata
abundata_reordered_forPCAtools <- DC_imp_clust_omicsdata_forPCAtools[ ,order_forPCAtools] 

# change abundance df to numeric
abundata_reordered_forPCAtools <- abundata_reordered_forPCAtools %>%
  mutate_all(as.numeric)

# Log transform
log2_abundata_forPCAtools <- log2(abundata_reordered_forPCAtools)

# remove outlier subj from both df
log2_abundata_forPCAtools <- log2_abundata_forPCAtools %>%
  dplyr::select(!contains("6106")) %>%
  dplyr::select(!contains("6112"))

metadata_forPCAtools <- metadata_forPCAtools %>%
  filter(Subject != 6106,
         Subject != 6112)

# unite pre_post column with intervention column to create pre_intervention column
metadata_forPCAtools <- metadata_forPCAtools %>%
  unite(col = "pre_post_intervention",
        c("pre_post","Intervention"),
        sep = "_",
        remove = FALSE)

PCA

# pca
p <- PCAtools::pca(log2_abundata_forPCAtools, 
         metadata = metadata_forPCAtools, 
         scale = FALSE # using scaled data already (log2 transformed)
         
)

# plot

PCAtools::biplot(p,
                 showLoadings = TRUE, # show variables that contribute the most to PCs
                 lab = NULL,
                 title = )

Screeplot analysis

Horn’s parallel analysis

horn <- parallelPCA(log2_abundata_forPCAtools)

horn$n
## [1] 10

Elbow method

elbow <- findElbowPoint(p$variance)

elbow
## PC6 
##   6
  screeplot(p,
    components = getComponents(p, 1:20),
    vline = c(horn$n, elbow)) +
  geom_label(aes(x = horn$n + 1, y = 50,
      label = 'Horn\'s', vjust = -1, size = 8)) +
    geom_label(aes(x = elbow + 1, y = 50,
      label = 'Elbow method', vjust = -3, size = 8))

How many PCs do we need to capture at least 80% variance?

which(cumsum(p$variance) > 80)[1]
## PC16 
##   16

More PCAs

Pre vs post both

PC1vPC2
biplot(p,
       lab = paste0(metadata_forPCAtools$Subject),
       colby = 'pre_post_intervention',
       colkey = c("pre_Yellow" = "yellow",
                  "post_Yellow" = "yellow4",
                  "pre_Red" = "red",
                  "post_Red" = "red4"),
       hline = 0, vline = 0,
       # ellipse config
       ellipse = TRUE,
       ellipseType = 't', # assumes multivariate t-distribution
       ellipseLevel = 0.95,
       ellipseFill = TRUE,
       ellipseAlpha = 0.2,
       ellipseLineSize = 0,
       xlim = c(-7,5), ylim = c(-5,5),
       legendPosition = 'right',
       title = "PCA Scores Plot",
       subtitle = "Log2 transformed data, HILIC (+), outliers removed, no QCs \n95% confidence level ellipses")

(PCA.colby.prevspost <- biplot(p,
                               lab = NULL,
                           colby = 'pre_post_intervention',
                           colkey = c("pre_Yellow" = "yellow",
                                      "post_Yellow" = "yellow4",
                                      "pre_Red" = "red",
                                      "post_Red" = "red4"),
                           hline = 0, vline = 0,
         legendPosition = 'right',
         title = "PCA Scores Plot",
         subtitle = "Log2 transformed data, HILIC (+), outliers removed, no QCs \n95% confidence level ellipses",
         showLoadings = TRUE))

Pairs plot
(PCA_pairsplot.colby.prevspost <-
  pairsplot(p,
    components = getComponents(p, c(1:10)),
    triangle = TRUE, trianglelabSize = 12,
    hline = 0, vline = 0,
    pointSize = 0.4,
    gridlines.major = FALSE, gridlines.minor = FALSE,
    colby = 'pre_post_intervention', 
    colkey = c("pre_Yellow" = "yellow",
               "post_Yellow" = "yellow4",
               "pre_Red" = "pink",
               "post_Red" = "red4"),
    title = 'Pairs plot', plotaxes = FALSE,
    margingaps = unit(c(-0.01, -0.01, -0.01, -0.01), 'cm')))

Sex

PC1vPC2

(PCA.colby.Sex <- biplot(p,
                           lab = paste0(metadata_forPCAtools$Subject),
                          colby = 'Sex',
                          colkey = c("M" = "red",
                                     "F" = "purple"),
                          hline = 0, vline = 0,
                          legendPosition = 'right' +
                            geom_point(aes(text = metadata_forPCAtools$Subject))))

ggplotly(PCA.colby.Sex,
         tooltip = "text") 

Pairsplot

  pairsplot(p,
    components = getComponents(p, c(1:10)),
    triangle = TRUE, trianglelabSize = 12,
    hline = 0, vline = 0,
    pointSize = 0.4,
    gridlines.major = FALSE, gridlines.minor = FALSE,
    colby = 'Sex', 
    colkey = c("M" = "red",
               "F" = "purple"),
    title = 'Pairs plot', plotaxes = FALSE,
    margingaps = unit(c(-0.01, -0.01, -0.01, -0.01), 'cm'))

Overall pre v post

PC1vPC2

(PCA.colby.overall.prevspost <- biplot(p,
                                       lab = paste0(metadata_forPCAtools$Subject),
                                       colby = 'pre_post',
                                       colkey = c("pre" = "orange",
                                                  "post" = "green3"),
                                       hline = 0, vline = 0,
                                       legendPosition = 'right' +
                                         geom_point(aes(text = metadata_forPCAtools$Subject))))

ggplotly(PCA.colby.overall.prevspost,
         tooltip = "text") 

Pairsplot

  pairsplot(p,
    components = getComponents(p, c(1:10)),
    triangle = TRUE, trianglelabSize = 12,
    hline = 0, vline = 0,
    pointSize = 0.4,
    gridlines.major = FALSE, gridlines.minor = FALSE,
    colby = 'pre_post', 
    colkey = c("pre" = "orange",
               "post" = "green3"),
    title = 'Pairs plot', plotaxes = FALSE,
    margingaps = unit(c(-0.01, -0.01, -0.01, -0.01), 'cm'))

Period

PC1vPC2

(PCA.colby.period <- biplot(p,
                            lab = paste0(metadata_forPCAtools$Subject),
                            colby = 'Period',
                            hline = 0, vline = 0,
                            legendPosition = 'right' +
                              geom_point(aes(text = metadata_forPCAtools$Subject))))

ggplotly(PCA.colby.period,
         tooltip = "text") 

Pairsplot

  pairsplot(p,
    components = getComponents(p, c(1:10)),
    triangle = TRUE, trianglelabSize = 12,
    hline = 0, vline = 0,
    pointSize = 0.4,
    gridlines.major = FALSE, gridlines.minor = FALSE,
    colby = 'Period',
    title = 'Pairs plot', plotaxes = FALSE,
    margingaps = unit(c(-0.01, -0.01, -0.01, -0.01), 'cm'))

Sequence

PC1vPC2

(PCA.colby.sequence <- biplot(p,
                            lab = paste0(metadata_forPCAtools$Subject),
                            colby = 'sequence',
                            hline = 0, vline = 0,
                            legendPosition = 'right' +
                              geom_point(aes(text = metadata_forPCAtools$Subject))))

ggplotly(PCA.colby.sequence,
         tooltip = "text") 

Pairsplot

  pairsplot(p,
    components = getComponents(p, c(1:10)),
    triangle = TRUE, trianglelabSize = 12,
    hline = 0, vline = 0,
    pointSize = 0.4,
    gridlines.major = FALSE, gridlines.minor = FALSE,
    colby = 'sequence',
    title = 'Pairs plot', plotaxes = FALSE,
    margingaps = unit(c(-0.01, -0.01, -0.01, -0.01), 'cm'))

Eigen corplots

This is a cool way to explore the correlations between the metadata and the PCs! I want to look at how the metavariables correlate with PCs that account for 80% variation in the dataset.

Again: How many PCs do we need to capture at least 80% variance?

which(cumsum(p$variance) > 80)[1]
## PC16 
##   16
  eigencorplot(p,
    components = getComponents(p, 1:15), # get components that account for 80% variance
    metavars = colnames(metadata_forPCAtools),
    col = c('darkblue', 'blue2', 'gray', 'red2', 'darkred'),
    cexCorval = 0.7,
    colCorval = 'white',
    fontCorval = 2,
    posLab = 'bottomleft',
    rotLabX = 45,
    posColKey = 'top',
    cexLabColKey = 1.5,
    scale = TRUE,
    main = 'PC1-14 metadata correlations',
    colFrame = 'white',
    plotRsquared = FALSE)

  eigencorplot(p,
    components = getComponents(p, 1:15),
    metavars = colnames(metadata_forPCAtools),
    col = c('white', 'cornsilk1', 'gold', 'forestgreen', 'darkgreen'),
    cexCorval = 1.2,
    fontCorval = 2,
    posLab = 'all',
    rotLabX = 45,
    scale = TRUE,
    main = bquote(Principal ~ component ~ Pearson ~ r^2 ~ metadata ~ correlates),
    plotRsquared = TRUE,
    corFUN = 'pearson',
    corUSE = 'pairwise.complete.obs',
    corMultipleTestCorrection = 'BH',
    signifSymbols = c('****', '***', '**', '*', ''),
    signifCutpoints = c(0, 0.0001, 0.001, 0.01, 0.05, 1))

I am most interested in PCs affected by pre_post_intervention, so I think it would be good to investigate the metabolites that contribute the most to these PCs.

Loadings plot

# loadings plot for PCs 8 and 9
  plotloadings(p,
    components = getComponents(p, c(8,9)),
    rangeRetain = 0.1, absolute = TRUE,
    col = c('black', 'pink', 'red4'),
    drawConnectors = TRUE, labSize = 3,
    title = "Loadings plot",
    subtitle = "PC 8 and PC 9",
    caption = "Pre_post_intervention is highly correlated with these PCs") + coord_flip()

Multilevel PCA

library(mixOmics)
Data_forMPCA <- DC_imp_metabind_clust_log2_noQCs %>%
  mutate_at("Subject", as.factor)
 

summary(as.factor(Data_forMPCA$Subject))
## 6101 6102 6103 6104 6105 6106 6108 6109 6110 6111 6112 6113 
##    4    4    4    4    4    4    4    4    4    4    4    4
# make a vector for meta variables
(metavar <- Data_forMPCA[,c(1:11)] %>%
    colnames())
##  [1] "sample_ID"             "Subject"               "Period"               
##  [4] "pre_post_intervention" "Intervention"          "pre_post"             
##  [7] "sequence"              "Intervention_week"     "Sex"                  
## [10] "Age"                   "BMI"

PCA w/ outliers

mixOmicsPCA.result <- mixOmics::pca(Data_forMPCA[,!names(Data_forMPCA) %in% metavar],
                            scale = FALSE,
                            center = FALSE)

plotIndiv(mixOmicsPCA.result, 
          ind.names = Data_forMPCA$Subject, 
          group = Data_forMPCA$pre_post_intervention, 
          legend = TRUE, 
          legend.title = "Treatment", 
          title = 'Regular PCA, HILIC (+), Log2 transformed')

Multilevel PCA

With all data

pre_post_intervention

multilevelPCA.result <- mixOmics::pca(Data_forMPCA[,-(c(1:11))], 
                            multilevel = Data_forMPCA$Subject,
                            scale = FALSE,
                            center = FALSE)

plotIndiv(multilevelPCA.result, 
          ind.names = Data_forMPCA$Subject, 
          group = Data_forMPCA$pre_post_intervention, 
          legend = TRUE, 
          legend.title = "Treatment", 
          title = 'Multilevel PCA, HILIC (+), Log2 transformed')

Loadings

plotLoadings(multilevelPCA.result, ndisplay = 75)

pre_post

multilevelPCA.result <- mixOmics::pca(Data_forMPCA[,-(c(1:11))], 
                            multilevel = Data_forMPCA$Subject,
                            scale = FALSE,
                            center = FALSE)

plotIndiv(multilevelPCA.result, 
          ind.names = Data_forMPCA$Subject, 
          group = Data_forMPCA$pre_post, 
          legend = TRUE, 
          legend.title = "Treatment", 
          title = 'Multilevel PCA, HILIC (+), Log2 transformed')

Loadings

plotLoadings(multilevelPCA.result, ndisplay = 75, comp = 2)

Univariate analysis

Wrangle data

use tidy data

# tidy df 
DC_imp_metabind_clust_tidy_log2 <- DC_imp_metabind_clust_log2 %>%
  pivot_longer(cols = 12:ncol(.),
               names_to = "mz_rt",
               values_to = "rel_abund_log2")

# use tidy data 
head(DC_imp_metabind_clust_tidy_log2)
## # A tibble: 6 × 13
##   sample_ID  Subject Period pre_post_intervention Intervention pre_post sequence
##   <chr>      <chr>   <chr>  <chr>                 <chr>        <chr>    <chr>   
## 1 6101_U1_H… 6101    U1     pre_Red               Red          pre      R_Y     
## 2 6101_U1_H… 6101    U1     pre_Red               Red          pre      R_Y     
## 3 6101_U1_H… 6101    U1     pre_Red               Red          pre      R_Y     
## 4 6101_U1_H… 6101    U1     pre_Red               Red          pre      R_Y     
## 5 6101_U1_H… 6101    U1     pre_Red               Red          pre      R_Y     
## 6 6101_U1_H… 6101    U1     pre_Red               Red          pre      R_Y     
## # ℹ 6 more variables: Intervention_week <chr>, Sex <chr>, Age <chr>, BMI <chr>,
## #   mz_rt <chr>, rel_abund_log2 <dbl>
# remove QCs
df_for_stats <- DC_imp_metabind_clust_tidy_log2 %>%
  filter(Intervention != "QC")

# check if QCs were removed
unique(df_for_stats$Intervention)
## [1] "Red"    "Yellow"
# df without outliers
df_for_stats_noOutlier <- df_for_stats %>%
  filter(Subject != "6106",
         Subject != "6112")

# check if outlier was removed
unique(df_for_stats_noOutlier$Subject)
##  [1] "6101" "6102" "6103" "6104" "6105" "6108" "6109" "6110" "6111" "6113"
# turn off sci notation outputs
options(scipen = 999)

Parametric tests

ANOVA (repeated measures) across timepoints

anova_outpout_df <- df_for_stats_noOutlier %>%
  dplyr::select(Subject, pre_post_intervention, mz_rt, rel_abund_log2) %>%
  group_by(mz_rt) %>%
  anova_test(rel_abund_log2 ~ pre_post_intervention, wid = Subject,
             detailed = TRUE) %>%
  adjust_pvalue(method = "BH") %>%
  as.data.frame()

anova_sig <- anova_outpout_df %>%
  filter(p.adj <= 0.05)

# how many significant features?
nrow(anova_sig)
## [1] 19

Heatmap of features significant by ANOVA

ANOVA_heatmap_data <- DC_imp_metabind_clust_log2_noQCs %>%
  unite("Subject_pre_post_intervention", Subject, pre_post_intervention, sep = "_", remove = FALSE) %>%
  dplyr::select(Subject, Subject_pre_post_intervention, pre_post, all_of(anova_sig$mz_rt)) %>%
  # remove outliers
  filter(Subject != 6106,
         Subject != 6112)

ANOVA_heatmap <- 
  pheatmap(ANOVA_heatmap_data[,-c(1:3)],
           scale = "column",
           cluster_rows = TRUE,
           clustering_distance_rows = "euclidean",
           clustering_distance_cols = "euclidean",
           clustering_method = "ward.D2",
           labels_row = ANOVA_heatmap_data$Subject_pre_post_intervention,
           color = colorRampPalette(c("#67a9cf", "#f7f7f7", "#ef8a62"))(16),
           main = "Heatmap of features significant across timepoints \nby repeated measures one-way ANOVA \nBenjamoni-Hochberg corrected p-values > 0.05 \nHILIC (+)")

ggsave(plot = ANOVA_heatmap, height = 13,
       filename = "plots and figures/ANOVA_sig_heatmap.svg")

Paired t tests

Here, I am comparing pre- to post-intervention for both yellow and tomato soy (Red) juice interventions. I also compare post-yellow to post-red intervention. I am using the log transformed values of rel abundance since parametric tests assume normality.

Ctrl

# run paired t-tests for control intervention
ctrl_t.test_paired <- df_for_stats %>%
  filter(Intervention == "Yellow") %>%
  dplyr::select(Subject, pre_post, mz_rt, rel_abund_log2) %>%
  group_by(mz_rt) %>%
  t_test(rel_abund_log2 ~ pre_post, 
         paired = TRUE, 
         p.adjust.method = "BH") %>% # Benjamini-Hochberg controlling to lower false positives
  add_significance()

Statistically significant features

# which features are significant?
ctrl_t.test_paired_sig <- ctrl_t.test_paired %>%
  filter(p <= 0.05)
tibble(ctrl_t.test_paired_sig)
## # A tibble: 228 × 10
##    mz_rt        .y.   group1 group2    n1    n2 statistic    df       p p.signif
##    <chr>        <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>   <dbl> <chr>   
##  1 104.0706_5.… rel_… post   pre       12    12     -3.56    11 0.00449 **      
##  2 104.0818_4.… rel_… post   pre       12    12     -3.06    11 0.0109  *       
##  3 115.0866_4.… rel_… post   pre       12    12     -3.47    11 0.00519 **      
##  4 121.0647_3.… rel_… post   pre       12    12     -3.80    11 0.00297 **      
##  5 123.0553_5   rel_… post   pre       12    12     -3.73    11 0.00332 **      
##  6 128.0819_2.… rel_… post   pre       12    12     -3.49    11 0.00509 **      
##  7 131.118_4.7… rel_… post   pre       12    12     -2.62    11 0.0236  *       
##  8 132.0771_6.… rel_… post   pre       12    12     -3.31    11 0.00689 **      
##  9 134.06_3.798 rel_… post   pre       12    12     -2.21    11 0.0495  *       
## 10 138.0556_4.… rel_… post   pre       12    12      2.44    11 0.033   *       
## # ℹ 218 more rows
# how many are significant?
nrow(ctrl_t.test_paired_sig)
## [1] 228

Ctrl no outlier

# run paired t-tests for control intervention
ctrl_noOutlier_t.test_paired <- df_for_stats_noOutlier %>%
  filter(Intervention == "Yellow") %>%
  dplyr::select(Subject, pre_post, mz_rt, rel_abund_log2) %>%
  group_by(mz_rt) %>%
  t_test(rel_abund_log2 ~ pre_post, 
         paired = TRUE, 
         p.adjust.method = "BH") %>% # Benjamini-Hochberg controlling to lower false positives
  add_significance()

Statistically significant features

# which features are significant?
ctrl_noOutlier_t.test_paired_sig <- ctrl_noOutlier_t.test_paired %>%
  filter(p <= 0.05)
tibble(ctrl_noOutlier_t.test_paired_sig)
## # A tibble: 178 × 10
##    mz_rt        .y.   group1 group2    n1    n2 statistic    df       p p.signif
##    <chr>        <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>   <dbl> <chr>   
##  1 104.0706_5.… rel_… post   pre       10    10     -4.89     9 8.55e-4 ***     
##  2 104.0818_4.… rel_… post   pre       10    10     -2.92     9 1.7 e-2 *       
##  3 115.0866_4.… rel_… post   pre       10    10     -2.94     9 1.66e-2 *       
##  4 121.0647_3.… rel_… post   pre       10    10     -4.14     9 2.54e-3 **      
##  5 123.0553_5   rel_… post   pre       10    10     -3.21     9 1.06e-2 *       
##  6 124.0759_2.… rel_… post   pre       10    10     -2.48     9 3.5 e-2 *       
##  7 128.0819_2.… rel_… post   pre       10    10     -3.36     9 8.44e-3 **      
##  8 130.0499_4.… rel_… post   pre       10    10     -2.39     9 4.08e-2 *       
##  9 132.0771_6.… rel_… post   pre       10    10     -2.64     9 2.71e-2 *       
## 10 137.071_3.7… rel_… post   pre       10    10     -2.30     9 4.68e-2 *       
## # ℹ 168 more rows
# how many are significant?
nrow(ctrl_noOutlier_t.test_paired_sig)
## [1] 178
Mummichog list
yellow_for_mummichog <- ctrl_noOutlier_t.test_paired %>%
  dplyr::select(mz_rt,
         p,
         statistic) %>%
  separate(col = mz_rt,
           into = c("m.z", "r.t."),
           sep = "_") %>%
  rename("t.score" = "statistic")

write_csv(yellow_for_mummichog,
          "yel-mummichog-list-hilicpos.csv")

Red

# run paired t-tests for control intervention
red_t.test_paired <- df_for_stats %>%
  filter(Intervention == "Red") %>%
  dplyr::select(Subject, pre_post, mz_rt, rel_abund_log2) %>%
  group_by(mz_rt) %>%
  t_test(rel_abund_log2 ~ pre_post, 
         paired = TRUE, 
         p.adjust.method = "BH") %>% # Benjamini-Hochberg controlling to lower false positives
  add_significance()

Statistically significant features

# which features are significant?
red_t.test_paired_sig <- red_t.test_paired %>%
  filter(p <= 0.05)
tibble(red_t.test_paired_sig)
## # A tibble: 142 × 10
##    mz_rt        .y.   group1 group2    n1    n2 statistic    df       p p.signif
##    <chr>        <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>   <dbl> <chr>   
##  1 116.1073_1.… rel_… post   pre       12    12     -3.50    11 4.95e-3 **      
##  2 116.1171_1.… rel_… post   pre       12    12     -3.34    11 6.58e-3 **      
##  3 138.0556_4.… rel_… post   pre       12    12      2.94    11 1.34e-2 *       
##  4 138.055_4.1… rel_… post   pre       12    12      5.83    11 1.15e-4 ***     
##  5 144.1021_3.… rel_… post   pre       12    12     -2.46    11 3.16e-2 *       
##  6 144.1028_4.… rel_… post   pre       12    12     -2.37    11 3.75e-2 *       
##  7 146.0812_0.… rel_… post   pre       12    12     -3.60    11 4.14e-3 **      
##  8 146.0813_1.… rel_… post   pre       12    12     -3.03    11 1.14e-2 *       
##  9 146.0921_5.… rel_… post   pre       12    12      2.99    11 1.22e-2 *       
## 10 150.0694_1.… rel_… post   pre       12    12     -2.61    11 2.41e-2 *       
## # ℹ 132 more rows
# how many are significant?
nrow(red_t.test_paired_sig)
## [1] 142

Red no outlier

# run paired t-tests for control intervention
red_noOutlier_t.test_paired <- df_for_stats_noOutlier %>%
  filter(Intervention == "Red") %>%
  dplyr::select(Subject, pre_post, mz_rt, rel_abund_log2) %>%
  group_by(mz_rt) %>%
  t_test(rel_abund_log2 ~ pre_post, 
         paired = TRUE, 
         p.adjust.method = "BH") %>% # Benjamini-Hochberg controlling to lower false positives
  add_significance()

Statistically significant features

# which features are significant?
red_noOutlier_t.test_paired_sig <- red_noOutlier_t.test_paired %>%
  filter(p <= 0.05)
tibble(red_noOutlier_t.test_paired_sig)
## # A tibble: 145 × 10
##    mz_rt        .y.   group1 group2    n1    n2 statistic    df       p p.signif
##    <chr>        <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>   <dbl> <chr>   
##  1 100.0757_0.… rel_… post   pre       10    10     -2.33     9 4.45e-2 *       
##  2 104.0818_4.… rel_… post   pre       10    10     -4.88     9 8.7 e-4 ***     
##  3 116.1073_1.… rel_… post   pre       10    10     -5.53     9 3.66e-4 ***     
##  4 116.1171_1.… rel_… post   pre       10    10     -2.97     9 1.57e-2 *       
##  5 118.0867_4.… rel_… post   pre       10    10     -2.40     9 3.97e-2 *       
##  6 138.0556_4.… rel_… post   pre       10    10      5.11     9 6.36e-4 ***     
##  7 138.055_4.1… rel_… post   pre       10    10      9.11     9 7.70e-6 ****    
##  8 144.1028_4.… rel_… post   pre       10    10     -2.44     9 3.76e-2 *       
##  9 146.0812_0.… rel_… post   pre       10    10     -3.34     9 8.63e-3 **      
## 10 146.0813_1.… rel_… post   pre       10    10     -3.02     9 1.44e-2 *       
## # ℹ 135 more rows
# how many are significant?
nrow(red_noOutlier_t.test_paired_sig)
## [1] 145
Mummichog list
red_for_mummichog <- red_noOutlier_t.test_paired %>%
  dplyr::select(mz_rt,
         p,
         statistic) %>%
  separate(col = mz_rt,
           into = c("m.z", "r.t."),
           sep = "_") %>%
  rename("t.score" = "statistic")

write_csv(red_for_mummichog,
          "red-mummichog-list-hilicpos.csv")

Post-red vs post-yellow

# run paired t-tests for post interventions
post_t.test_paired <- df_for_stats %>%
  filter(pre_post == "post") %>%
  dplyr::select(Subject, Intervention, mz_rt, rel_abund_log2) %>%
  group_by(mz_rt) %>%
  t_test(rel_abund_log2 ~ Intervention, 
         paired = TRUE, 
         p.adjust.method = "BH") %>% # Benjamini-Hochberg controlling to lower false positives
  add_significance()

Statistically significant features

# which features are significant?
post_t.test_paired_sig <- post_t.test_paired %>%
  filter(p <= 0.05)
tibble(post_t.test_paired_sig)
## # A tibble: 38 × 10
##    mz_rt        .y.   group1 group2    n1    n2 statistic    df       p p.signif
##    <chr>        <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>   <dbl> <chr>   
##  1 104.0706_5.… rel_… Red    Yellow    12    12      2.43    11 0.0335  *       
##  2 121.0647_3.… rel_… Red    Yellow    12    12      2.21    11 0.049   *       
##  3 137.071_3.7… rel_… Red    Yellow    12    12      3.27    11 0.00748 **      
##  4 145.1699_7.… rel_… Red    Yellow    12    12     -2.39    11 0.0357  *       
##  5 146.0925_2.… rel_… Red    Yellow    12    12      2.58    11 0.0258  *       
##  6 147.0474_4.… rel_… Red    Yellow    12    12      2.81    11 0.017   *       
##  7 153.1273_3.… rel_… Red    Yellow    12    12     -2.48    11 0.0307  *       
##  8 182.0811_5.… rel_… Red    Yellow    12    12      3.29    11 0.00719 **      
##  9 188.0705_0.… rel_… Red    Yellow    12    12     -2.30    11 0.0422  *       
## 10 202.1187_4.… rel_… Red    Yellow    12    12      2.41    11 0.0344  *       
## # ℹ 28 more rows
# how many are significant?
nrow(post_t.test_paired_sig)
## [1] 38

Post-red vs post-yellow no Outlier

# run paired t-tests for post interventions
post_noOutlier_t.test_paired <- df_for_stats %>%
  filter(pre_post == "post",
         Subject != "6106") %>%
  dplyr::select(Subject, Intervention, mz_rt, rel_abund_log2) %>%
  group_by(mz_rt) %>%
  t_test(rel_abund_log2 ~ Intervention, 
         paired = TRUE, 
         p.adjust.method = "BH") %>% # Benjamini-Hochberg controlling to lower false positives
  add_significance()

Statistically significant features

# which features are significant?
post_noOutlier_t.test_paired_sig <- post_noOutlier_t.test_paired %>%
  filter(p <= 0.05)
tibble(post_noOutlier_t.test_paired_sig)
## # A tibble: 44 × 10
##    mz_rt         .y.   group1 group2    n1    n2 statistic    df      p p.signif
##    <chr>         <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>  <dbl> <chr>   
##  1 104.0706_5.3… rel_… Red    Yellow    11    11      3.04    10 0.0125 *       
##  2 128.0819_2.6… rel_… Red    Yellow    11    11      2.47    10 0.0331 *       
##  3 142.0863_0.6… rel_… Red    Yellow    11    11      2.31    10 0.0435 *       
##  4 143.0816_5.3… rel_… Red    Yellow    11    11      3.35    10 0.0074 **      
##  5 144.1021_3.8… rel_… Red    Yellow    11    11     -2.41    10 0.0366 *       
##  6 146.0925_2.6… rel_… Red    Yellow    11    11      3.06    10 0.012  *       
##  7 147.0474_4.9… rel_… Red    Yellow    11    11      2.74    10 0.0209 *       
##  8 153.1273_3.8… rel_… Red    Yellow    11    11     -2.32    10 0.0431 *       
##  9 170.1005_6.9… rel_… Red    Yellow    11    11     -2.50    10 0.0315 *       
## 10 182.0811_5.07 rel_… Red    Yellow    11    11      2.28    10 0.0456 *       
## # ℹ 34 more rows
# how many are significant?
nrow(post_noOutlier_t.test_paired_sig)
## [1] 44
Mummichog list
post_for_mummichog <- post_noOutlier_t.test_paired %>%
  dplyr::select(mz_rt,
         p,
         statistic) %>%
  separate(col = mz_rt,
           into = c("m.z", "r.t"),
           sep = "_") %>%
  rename("p.value" = "p") %>%
  rename("t.score" = "statistic")

write_csv(post_for_mummichog,
          "post-mummichog-list-hilicpos.csv")

Outlier comparison

Are there any significant features shared between tests with and without outlier?

post_sig_outlier_comp <- list(post_noOutlier_t.test_paired_sig,
                              post_t.test_paired_sig) %>%
  reduce(inner_join, by = "mz_rt")

tibble(post_sig_outlier_comp)
## # A tibble: 29 × 19
##    mz_rt          .y..x  group1.x group2.x  n1.x  n2.x statistic.x  df.x     p.x
##    <chr>          <chr>  <chr>    <chr>    <int> <int>       <dbl> <dbl>   <dbl>
##  1 104.0706_5.342 rel_a… Red      Yellow      11    11        3.04    10 0.0125 
##  2 146.0925_2.629 rel_a… Red      Yellow      11    11        3.06    10 0.012  
##  3 147.0474_4.904 rel_a… Red      Yellow      11    11        2.74    10 0.0209 
##  4 153.1273_3.887 rel_a… Red      Yellow      11    11       -2.32    10 0.0431 
##  5 182.0811_5.07  rel_a… Red      Yellow      11    11        2.28    10 0.0456 
##  6 188.0705_0.471 rel_a… Red      Yellow      11    11       -4.06    10 0.00229
##  7 208.0968_0.848 rel_a… Red      Yellow      11    11       -3.49    10 0.00587
##  8 229.1547_3.637 rel_a… Red      Yellow      11    11        2.58    10 0.0275 
##  9 229.1553_4.5   rel_a… Red      Yellow      11    11        2.71    10 0.0219 
## 10 230.1247_5.055 rel_a… Red      Yellow      11    11        4.04    10 0.00236
## # ℹ 19 more rows
## # ℹ 10 more variables: p.signif.x <chr>, .y..y <chr>, group1.y <chr>,
## #   group2.y <chr>, n1.y <int>, n2.y <int>, statistic.y <dbl>, df.y <dbl>,
## #   p.y <dbl>, p.signif.y <chr>
# how many sig features are shared between df vs df w/o outliers
nrow(post_sig_outlier_comp)
## [1] 29
# return sig features present only in df with outlier, and not in df without outlier
tibble(anti_join(post_noOutlier_t.test_paired_sig,
          post_t.test_paired_sig,
          by = "mz_rt"))
## # A tibble: 15 × 10
##    mz_rt         .y.   group1 group2    n1    n2 statistic    df      p p.signif
##    <chr>         <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>  <dbl> <chr>   
##  1 128.0819_2.6… rel_… Red    Yellow    11    11      2.47    10 0.0331 *       
##  2 142.0863_0.6… rel_… Red    Yellow    11    11      2.31    10 0.0435 *       
##  3 143.0816_5.3… rel_… Red    Yellow    11    11      3.35    10 0.0074 **      
##  4 144.1021_3.8… rel_… Red    Yellow    11    11     -2.41    10 0.0366 *       
##  5 170.1005_6.9… rel_… Red    Yellow    11    11     -2.50    10 0.0315 *       
##  6 227.1262_3.5… rel_… Red    Yellow    11    11      2.44    10 0.0351 *       
##  7 227.1263_3.4… rel_… Red    Yellow    11    11      2.45    10 0.0345 *       
##  8 241.0948_5.1… rel_… Red    Yellow    11    11      2.27    10 0.0464 *       
##  9 243.1339_2.7… rel_… Red    Yellow    11    11      2.49    10 0.0322 *       
## 10 274.204_0.853 rel_… Red    Yellow    11    11     -2.55    10 0.029  *       
## 11 286.1283_4.1… rel_… Red    Yellow    11    11      2.72    10 0.0216 *       
## 12 324.2159_0.4… rel_… Red    Yellow    11    11     -2.55    10 0.0291 *       
## 13 356.2792_0.4… rel_… Red    Yellow    11    11     -2.60    10 0.0264 *       
## 14 406.1702_4.5… rel_… Red    Yellow    11    11     -2.47    10 0.0333 *       
## 15 86.06_5.38    rel_… Red    Yellow    11    11      2.25    10 0.0478 *
# return sig features from df without outlier that are also present in df with outlier
kable(semi_join(post_noOutlier_t.test_paired_sig,
          post_t.test_paired_sig,
          by = "mz_rt"))
mz_rt .y. group1 group2 n1 n2 statistic df p p.signif
104.0706_5.342 rel_abund_log2 Red Yellow 11 11 3.036895 10 0.0125000 *
146.0925_2.629 rel_abund_log2 Red Yellow 11 11 3.061303 10 0.0120000 *
147.0474_4.904 rel_abund_log2 Red Yellow 11 11 2.736832 10 0.0209000 *
153.1273_3.887 rel_abund_log2 Red Yellow 11 11 -2.315656 10 0.0431000 *
182.0811_5.07 rel_abund_log2 Red Yellow 11 11 2.282133 10 0.0456000 *
188.0705_0.471 rel_abund_log2 Red Yellow 11 11 -4.059869 10 0.0022900 **
208.0968_0.848 rel_abund_log2 Red Yellow 11 11 -3.485203 10 0.0058700 **
229.1547_3.637 rel_abund_log2 Red Yellow 11 11 2.577394 10 0.0275000 *
229.1553_4.5 rel_abund_log2 Red Yellow 11 11 2.711421 10 0.0219000 *
230.1247_5.055 rel_abund_log2 Red Yellow 11 11 4.039951 10 0.0023600 **
230.1863_4.368 rel_abund_log2 Red Yellow 11 11 2.928834 10 0.0151000 *
234.1136_0.825 rel_abund_log2 Red Yellow 11 11 -6.021853 10 0.0001280 ***
241.1547_4.545 rel_abund_log2 Red Yellow 11 11 2.592767 10 0.0268000 *
254.1498_4.993 rel_abund_log2 Red Yellow 11 11 8.107799 10 0.0000105 ****
259.0977_3.914 rel_abund_log2 Red Yellow 11 11 17.233573 10 0.0000000 ****
267.1102_4.521 rel_abund_log2 Red Yellow 11 11 2.423222 10 0.0359000 *
267.1104_4.411 rel_abund_log2 Red Yellow 11 11 2.312584 10 0.0433000 *
280.1183_0.99 rel_abund_log2 Red Yellow 11 11 -2.528365 10 0.0300000 *
303.1914_5.064 rel_abund_log2 Red Yellow 11 11 -2.722475 10 0.0215000 *
312.2284_1.053 rel_abund_log2 Red Yellow 11 11 -2.396897 10 0.0375000 *
431.0968_4.511 rel_abund_log2 Red Yellow 11 11 20.295177 10 0.0000000 ****
433.1123_4.289 rel_abund_log2 Red Yellow 11 11 13.494735 10 0.0000001 ****
433.2365_1.214 rel_abund_log2 Red Yellow 11 11 10.921157 10 0.0000007 ****
447.0919_4.001 rel_abund_log2 Red Yellow 11 11 12.134606 10 0.0000003 ****
449.1074_3.962 rel_abund_log2 Red Yellow 11 11 2.288715 10 0.0451000 *
461.1072_4.293 rel_abund_log2 Red Yellow 11 11 27.697228 10 0.0000000 ****
469.0527_4.611 rel_abund_log2 Red Yellow 11 11 17.117670 10 0.0000000 ****
540.4236_0.454 rel_abund_log2 Red Yellow 11 11 -5.762674 10 0.0001820 ***
607.1285_8.059 rel_abund_log2 Red Yellow 11 11 24.091233 10 0.0000000 ****

Standard comparisons

Here, I want to only focus on the metabolites that I hypothesized to change. This way I can avoid multiple correction adjustments and see if I can capture any significant differences at different timepoints

Wrangle

stds_df_for_stats_wide <- df_for_stats %>%
  pivot_wider(names_from = mz_rt,
              values_from = rel_abund_log2) %>%
  dplyr::select(c(1:11),
                "118.0867_4.678", #valine
                "147.0765_6.582", #glutamine
                "162.1129_5.651", #l-carnitine
                "166.0861_4.404", #phenylalanine
                "182.0808_5.647", #tyrosine
                "147.1129_8.251", #lysine
                "156.0772_7.669", #histidine
                "205.0972_4.752", #tryptophan
                "175.119_8.106", #arginine
                "241.0311_8.284" #cystine
                )

# make tidy df
stds_df_forstats_tidy <- stds_df_for_stats_wide %>%
  pivot_longer(cols = 12:ncol(.),
               names_to = "mz_rt",
               values_to = "rel_abund_log2")
# changing factor levels for pre_post_intervention
stds_df_forstats_tidy$pre_post_intervention <- factor(stds_df_forstats_tidy$pre_post_intervention,
                              levels = c("pre_Yellow", "post_Yellow", "pre_Red", "post_Red"))

levels(stds_df_forstats_tidy$pre_post_intervention)  
## [1] "pre_Yellow"  "post_Yellow" "pre_Red"     "post_Red"

Boxplots

stds_df_forstats_tidy %>% 
  ggplot(aes(x = pre_post_intervention, y = rel_abund_log2, fill = Intervention)) +
  geom_boxplot(outlier.shape = NA) +
  scale_fill_manual(values = c("Yellow" = "gold",
                                           "Red" = "tomato1")) +
  geom_line(aes(group = Subject), colour = "purple", linewidth = 0.2) +
  facet_wrap(vars(mz_rt), scales = "free_y") + 
  theme_bw() 

Control t-test

# run paired t-tests for before vs. aftet control intervention
stds_ctrl_t.test_paired <- stds_df_forstats_tidy %>%
  filter(Intervention == "Yellow") %>%
  dplyr::select(Subject, pre_post, mz_rt, rel_abund_log2) %>%
  filter(Subject != c(6106, 6112)) %>% # remove outliers
  group_by(mz_rt) %>%
  t_test(rel_abund_log2 ~ pre_post, 
         paired = TRUE) %>% 
  add_significance()

Statistically significant features

# which features are significant?
stds_ctrl_t.test_paired_sig <- stds_ctrl_t.test_paired %>%
  filter(p <= 0.05)
tibble(stds_ctrl_t.test_paired_sig)
## # A tibble: 3 × 10
##   mz_rt         .y.   group1 group2    n1    n2 statistic    df       p p.signif
##   <chr>         <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>   <dbl> <chr>   
## 1 162.1129_5.6… rel_… post   pre       11    11     -3.07    10 0.0118  *       
## 2 166.0861_4.4… rel_… post   pre       11    11     -3.20    10 0.00944 **      
## 3 205.0972_4.7… rel_… post   pre       11    11     -2.48    10 0.0324  *
# how many are significant?
nrow(stds_ctrl_t.test_paired_sig)
## [1] 3

Red t-tests

# run paired t-tests for before vs. after red intervention
stds_red_t.test_paired <- stds_df_forstats_tidy %>%
  filter(Intervention == "Red") %>%
  dplyr::select(Subject, pre_post, mz_rt, rel_abund_log2) %>%
  filter(Subject != c(6106, 6112)) %>%
  group_by(mz_rt) %>%
  t_test(rel_abund_log2 ~ pre_post, 
         paired = TRUE) %>% 
  add_significance()

Statistically significant features

# which features are significant?
stds_red_t.test_paired_sig <- stds_red_t.test_paired %>%
  filter(p <= 0.05)
tibble(stds_red_t.test_paired_sig)
## # A tibble: 2 × 10
##   mz_rt          .y.   group1 group2    n1    n2 statistic    df      p p.signif
##   <chr>          <chr> <chr>  <chr>  <int> <int>     <dbl> <dbl>  <dbl> <chr>   
## 1 205.0972_4.752 rel_… post   pre       11    11     -2.34    10 0.0414 *       
## 2 241.0311_8.284 rel_… post   pre       11    11     -2.40    10 0.0375 *
# how many are significant?
nrow(stds_red_t.test_paired_sig)
## [1] 2

Intervention t-tests

# run paired t-tests for post-red vs. post-control intervention
stds_post_t.test_paired <- stds_df_forstats_tidy %>%
  filter(pre_post == "post") %>%
  dplyr::select(Subject, pre_post_intervention, mz_rt, rel_abund_log2) %>%
  filter(Subject != c(6106, 6112)) %>%
  group_by(mz_rt) %>%
  t_test(rel_abund_log2 ~ pre_post_intervention, 
         paired = TRUE) %>% 
  add_significance()

Statistically significant features

# which features are significant?
stds_post_t.test_paired_sig <- stds_post_t.test_paired %>%
  filter(p <= 0.05)
tibble(stds_post_t.test_paired_sig)
## # A tibble: 0 × 10
## # ℹ 10 variables: mz_rt <chr>, .y. <chr>, group1 <chr>, group2 <chr>, n1 <int>,
## #   n2 <int>, statistic <dbl>, df <dbl>, p <dbl>, p.signif <chr>
# how many are significant?
nrow(stds_post_t.test_paired_sig)
## [1] 0

Volcano plots

Post-intervention comparisons

Wrangle (no outlier)
# add regular relative abundance levels back into the df
df_for_stats <- df_for_stats %>%
  mutate(rel_abund = 2^(rel_abund_log2))

# calculate mean rel abund (not log) by sample, and avg fold change (FC) diff by feature
red_v_yel_volcano_data_noOutlier <- df_for_stats %>%
  filter(pre_post == "post",
         Subject != 6106,
         Subject != 6112) %>% # remove outlier subj
  group_by(Intervention, mz_rt) %>%
  summarize(mean_rel_abund = mean(rel_abund)) %>%
  pivot_wider(names_from = Intervention, values_from = mean_rel_abund) %>%
  mutate(FC_postRed_div_postYellow = Red/Yellow) 

# bind back pval
red_v_yel_tobind_noOutlier <- post_noOutlier_t.test_paired %>%
  dplyr::select(p)

# calculate log2FC, and -log10p
red_v_yel_volcano_data_noOutlier <- 
  bind_cols(red_v_yel_volcano_data_noOutlier, red_v_yel_tobind_noOutlier) %>%
  mutate(log2_FC_postRed_div_postYellow = if_else(FC_postRed_div_postYellow > 0,
                                                  log2(FC_postRed_div_postYellow),
                                                  -(log2(abs(FC_postRed_div_postYellow)))), 
         neglog10p = -log10(p))


# create a df of features passing FC and pval cutoffs higher in post-Red
postRed_sig_red_v_yel_volcano_noOutlier <- red_v_yel_volcano_data_noOutlier %>%
  filter(p <= 0.05 & log2_FC_postRed_div_postYellow >= 0.6)

# create a df of features passing FC and pval cutoffs higher in post-control
postYellow_sig_red_v_yel_volcano_noOutlier <- red_v_yel_volcano_data_noOutlier %>%
  filter(p <= 0.05 & log2_FC_postRed_div_postYellow <= -0.6)  
Export sig features

Create feature lists with significant features along with their clusters

#post-Red list
postRed_sig_intrvntn_comp_clusters <- left_join(postRed_sig_red_v_yel_volcano_noOutlier,
                                                cluster_features,
                                                by = "mz_rt")



#post-Yellow list
postYellow_sig_intrvntn_comp_clusters <- left_join(postYellow_sig_red_v_yel_volcano_noOutlier, cluster_features,
                                               by = "mz_rt")
# export list : sig higher post red 
write_csv(postRed_sig_intrvntn_comp_clusters,
          "Feature lists/postRed-sigfeatures-intervntn-comp.csv")

# export list : sig higher post yellow 
write_csv(postYellow_sig_intrvntn_comp_clusters,
          "Feature lists/postYellow-sigfeatures-intervntn-comp.csv")
Plot
(red_v_yellow_volcano_noOutlier <- red_v_yel_volcano_data_noOutlier %>%
  ggplot(aes(x = log2_FC_postRed_div_postYellow, y = neglog10p, 
             text = glue("Mass_retention time: {mz_rt}
                         P-value: {p}
                         Fold change tomato/control: {round(FC_postRed_div_postYellow, 2)}"))) +
  geom_point(color = "grey") +
  geom_point(data = postRed_sig_intrvntn_comp_clusters, 
             aes(x = log2_FC_postRed_div_postYellow, y = neglog10p),
             color = "tomato") +
  geom_point(data = postYellow_sig_intrvntn_comp_clusters, 
             aes(x = log2_FC_postRed_div_postYellow, y = neglog10p),
             color = "yellow2") +
  geom_vline(xintercept = 0.6, linetype = "dashed", color = "grey") +
  geom_vline(xintercept = -0.6, linetype = "dashed", color = "grey") +
  geom_hline(yintercept = 1.3, linetype = "dashed", color = "grey") +
  coord_cartesian(xlim = c(-5, 8)) +
  labs(title = "Volcano Plot of Features Different in People After Tomato-Soy and Control Juice Consumption",
       subtitle = "Red points are higher after tomato-soy juice consumption while yellow points are higher \nafter control tomato juice consumption. Subjects 6106 and 6112 removed",
       caption = "Vertical dashed lines represent a log2 fold change > 0.6 or < -0.6, and horizontal dashed line represents an FDR corrected \np-value of 0.05.",
       x = "Log2 Fold Change (TomatoSoy/Control)",
       y = "-Log10(P-value)") +
  theme_bw() +
  theme(plot.title = element_text(size = 12, hjust = 0),
        plot.caption = element_text(hjust = 0.5)))

(red_v_yellow_volcano_ggplotly_noOutlier <- ggplotly(red_v_yellow_volcano_noOutlier, tooltip = "text"))

Save plots

# save volcano plot
ggsave(plot = red_v_yellow_volcano_noOutlier,
       filename = "plots and figures/volcano plots/red_v_yellow_volcano_noOutlier.svg")

# save interactive volcano plot
saveWidget(widget = red_v_yellow_volcano_ggplotly_noOutlier,
           file = "plots and figures/volcano plots/interactive_redvyellow_volcano_plot_noOutlier.html")

Red

Wrangle (no outlier)
# calculate mean rel abund (not log) by sample, and avg fold change (FC) diff by feature
red_volcano_data_noOutlier <- df_for_stats %>%
  filter(Intervention == "Red",
         Subject != 6106,
         Subject != 6112) %>%
  group_by(pre_post, mz_rt) %>%
  summarize(mean_rel_abund = mean(rel_abund)) %>%
  pivot_wider(names_from = pre_post, values_from = mean_rel_abund) %>%
  mutate(FC_post_div_pre = post/pre) 

# bind back pval
red_tobind_noOutlier <- red_noOutlier_t.test_paired %>%
  dplyr::select(p)

# calculate log2FC, and -log10p
red_volcano_data_noOutlier <- 
  bind_cols(red_volcano_data_noOutlier, red_tobind_noOutlier) %>%
  mutate(log2_FC_post_div_pre = log2(FC_post_div_pre),
         neglog10p = -log10(p))


# create a df of features passing FC and pval cutoffs higher in post-intervention compared to pre
red_pre_v_post_volcano_noOutlier <- red_volcano_data_noOutlier %>%
  filter(p <= 0.05 & abs(log2_FC_post_div_pre) >= 0.6)
Export sig features

Create feature lists with significant features along with their clusters

red_sig_prepost_comp_clusters <- left_join(red_pre_v_post_volcano_noOutlier, cluster_features,
                                               by = "mz_rt")
# export 
write_csv(red_sig_prepost_comp_clusters,
          "Feature lists/Red-sigfeatures-PrevsPost-noOutliers.csv")
Plot
(red_volcano_noOutlier <- red_volcano_data_noOutlier %>%
  ggplot(aes(x = log2_FC_post_div_pre, y = neglog10p, 
             text = glue("Mass_retention time: {mz_rt}
                         P-value: {p}
                         Fold change post/pre: {round(FC_post_div_pre, 2)}"))) +
  geom_point(color = "grey") +
  geom_point(data = red_sig_prepost_comp_clusters, 
             aes(x = log2_FC_post_div_pre, y = neglog10p),
             color = "tomato") +
  geom_vline(xintercept = 0.6, linetype = "dashed", color = "grey") +
  geom_vline(xintercept = -0.6, linetype = "dashed", color = "grey") + 
  geom_hline(yintercept = 1.3, linetype = "dashed", color = "grey") +
  coord_cartesian(xlim = c(-5, 6)) +
  labs(title = "Volcano Plot of Features Different in People After Tomato-Soy Juice Consumption",
       subtitle = "Red points are higher after tomato-soy juice consumption when compared to prior to consumption. \nSubjects 6106 and 6112 removed",
       caption = "Vertical dashed lines represent an abs(log fold change) > 0.6, and horizontal dashed line represents an FDR corrected \np-value of 0.05.",
       x = "Log2 Fold Change (Post/Pre)",
       y = "-Log10(P-value)") +
  theme_bw() +
  theme(plot.title = element_text(size = 12, hjust = 0),
        plot.caption = element_text(hjust = 0.5)))

(red_volcano_ggplotly_noOutlier <- ggplotly(red_volcano_noOutlier, tooltip = "text"))

Save plots

# save volcano plot
ggsave(plot = red_volcano_noOutlier,
       filename = "plots and figures/volcano plots/red_volcano_noOutlier.svg")

# save interactive volcano plot
saveWidget(widget = red_volcano_ggplotly_noOutlier,
           file = "plots and figures/volcano plots/interactive_red_volcano_plot_noOutlier.html")

Yellow

Wrangle (no outlier)
# calculate mean rel abund (not log) by sample, and avg fold change (FC) diff by feature
yel_volcano_data_noOutlier <- df_for_stats %>%
  filter(Intervention == "Yellow",
         Subject != 6106,
         Subject != 6112) %>%
  group_by(pre_post, mz_rt) %>%
  summarize(mean_rel_abund = mean(rel_abund)) %>%
  pivot_wider(names_from = pre_post, values_from = mean_rel_abund) %>%
  mutate(FC_post_div_pre = post/pre) 

# bind back pval
yel_tobind_noOutlier <- ctrl_noOutlier_t.test_paired %>%
  dplyr::select(p)

# calculate log2FC, and -log10p
yel_volcano_data_noOutlier <- 
  bind_cols(yel_volcano_data_noOutlier, yel_tobind_noOutlier) %>%
  mutate(log2_FC_post_div_pre = log2(FC_post_div_pre),
         neglog10p = -log10(p))


# create a df of features passing FC and pval cutoffs higher in post-intervention compared to pre
yel_pre_v_post_volcano_noOutlier <- yel_volcano_data_noOutlier %>%
  filter(p <= 0.05 & abs(log2_FC_post_div_pre) >= 0.6)
Export sig features

Create feature lists with significant features along with their clusters

yel_sig_prepost_comp_clusters <- left_join(yel_pre_v_post_volcano_noOutlier, cluster_features,
                                               by = "mz_rt")
# export
write_csv(yel_sig_prepost_comp_clusters,
          "Yellow-sigfeatures-PrevsPost-noOutliers.csv")
Plot
(yel_volcano_noOutlier <- yel_volcano_data_noOutlier %>%
  ggplot(aes(x = log2_FC_post_div_pre, y = neglog10p, 
             text = glue("Mass_retention time: {mz_rt}
                         P-value: {p}
                         Fold change post/pre: {round(FC_post_div_pre, 2)}"))) +
  geom_point(color = "grey") +
  geom_point(data = yel_sig_prepost_comp_clusters, 
             aes(x = log2_FC_post_div_pre, y = neglog10p),
             color = "yellow2") +
  geom_vline(xintercept = 0.6, linetype = "dashed", color = "grey") +
  geom_vline(xintercept = -0.6, linetype = "dashed", color = "grey") +
  geom_hline(yintercept = 1.3, linetype = "dashed", color = "grey") +
  coord_cartesian(xlim = c(-6, 6)) +
  labs(title = "Volcano Plot of Features Different in People After Control, Yellow Tomato Juice Consumption",
       subtitle = "Yellow points are higher after control juice consumption when compared to prior to consumption.\nSubjects 6106 and 6112 removed",
       caption = "Vertical dashed lines represent abs(log2 fold change) > 0.6, and horizontal dashed line represents an FDR corrected \np-value of 0.05.",
       x = "Log2 Fold Change (Post/Pre)",
       y = "-Log10(P-value)") +
  theme_bw() +
  theme(plot.title = element_text(size = 12, hjust = 0),
        plot.caption = element_text(hjust = 0.5)))

(yel_volcano_ggplotly_noOutlier <- ggplotly(yel_volcano_noOutlier, tooltip = "text"))

Save plots

# save volcano plot
ggsave(plot = yel_volcano_noOutlier,
       filename = "plots and figures/volcano plots/yel_volcano_noOutlier.svg")

# save interactive volcano plot
saveWidget(widget = yel_volcano_ggplotly_noOutlier,
           file = "plots and figures/volcano plots/interactive_yel_volcano_plot_noOutlier.html")

Joined lists

Here, I want to venn significant features before I begin to look more into them. I am interested in the following effects: tomato effect, lycopene and/or soy isoflavones effect.

Tomato effect

  1. Tomato effect: join a list that only keeps features that are both significant in pre vs. post-red and pre vs. post-yellow.
# keep only features present in both pre vs post red and pre vs post yellow
tomato_effect <- inner_join(red_sig_prepost_comp_clusters,
                            yel_sig_prepost_comp_clusters,
                            by = "mz_rt")
dim(tomato_effect)
## [1] 12 25

Export venned list

write_csv(tomato_effect,
          "Feature lists/sig-features-tomato-effect.csv")

Mummichog matches

# read in hits from red 
red_mummichog_hits <- read_csv("../mummichog analysis/red-hilic-mixed mode results-integ/red-hilic-mummichog_matched_compound_all.csv") %>%
  mutate(Query.Mass = round(Query.Mass, digits = 4)) %>% # keep 4 decimal points for mass
  unite(mz_rt,
        c("Query.Mass", "Retention.Time"),
        sep = "_")

Mummichog makes a list of hits for all of the compounds, so we only need to do an inner join for one of the lists. The outcome would be the same for all of the lists used since I have to input my whole dataset (no cutoffs) into analysis.

mummichog_mass_matches_tomato <- inner_join(tomato_effect,
                                            red_mummichog_hits,
                                            by = "mz_rt")

length(unique(mummichog_mass_matches_tomato$mz_rt)) # how many?
## [1] 4
tibble(unique(mummichog_mass_matches_tomato$mz_rt)) # which features have a hit with mummichog?
## # A tibble: 4 × 1
##   `unique(mummichog_mass_matches_tomato$mz_rt)`
##   <chr>                                        
## 1 182.0811_5.07                                
## 2 255.0974_5.375                               
## 3 341.1704_5.083                               
## 4 86.06_5.38
# map KEGG IDs to compound names
tomato_KEGGmatches <- as.data.frame(cpdkegg2name(mummichog_mass_matches_tomato$Matched.Compound))

mummichog_mass_matches_tomato$NAME <- tomato_KEGGmatches$NAME

Boxplots

all sig tomato features
# metabs with pval < 0.05 and fc >= 1.51
sigmetabs_tomato_effect <- as.character(tomato_effect$mz_rt)
tomato_effect_df_for_stats_wide <- df_for_stats %>%
  dplyr::select(!rel_abund) %>%
  pivot_wider(names_from = mz_rt,
              values_from = rel_abund_log2) %>%
  dplyr::select(c(1:11),
                sigmetabs_tomato_effect)

# make tidy df
tomato_effect_df_for_stats_tidy <- tomato_effect_df_for_stats_wide %>%
  pivot_longer(cols = 12:ncol(.),
               names_to = "mz_rt",
               values_to = "rel_abund_log2")
# changing factor levels for pre_post_intervention
tomato_effect_df_for_stats_tidy$pre_post_intervention <- factor(tomato_effect_df_for_stats_tidy$pre_post_intervention,
                              levels = c("pre_Yellow", "post_Yellow", "pre_Red", "post_Red"))

levels(tomato_effect_df_for_stats_tidy$pre_post_intervention)  
## [1] "pre_Yellow"  "post_Yellow" "pre_Red"     "post_Red"
tomato_effect_df_for_stats_tidy %>% 
  ggplot(aes(x = pre_post_intervention, y = rel_abund_log2, fill = Intervention)) +
  geom_boxplot(outlier.shape = NA) +
  scale_fill_manual(values = c("Yellow" = "gold",
                                           "Red" = "tomato1")) +
  geom_line(aes(group = Subject), fill = "brown", linewidth = 0.2) +
  facet_wrap(vars(mz_rt)) + 
  theme_bw() 

Lyc/soy effect

  1. lycopene and/or soy isoflavones effect: join a list that only keeps features that are:
  • significantly different between post-Red and post-Yellow,
  • and significant between pre- and post-Red.
  • remove features that are significant between pre-Yellow and post-Yellow.
# combine sig features from post-red vs post-yellow
sig_postintervention_noOutlier <- full_join(postRed_sig_intrvntn_comp_clusters,
                                               postYellow_sig_intrvntn_comp_clusters)
dim(sig_postintervention_noOutlier)
## [1] 35 13
# select only sig features that are present when comparing pre-Red to post-Red
lyc_soy_effect <- inner_join(sig_postintervention_noOutlier,
                             red_sig_prepost_comp_clusters,
                             by = "mz_rt") 
dim(lyc_soy_effect)
## [1] 16 25
# remove features that are significant pre-Yellow vs post-Yellow comparison
# lyc_soy_effect <- anti_join(lyc_soy_effect,
#                             yel_sig_prepost_comp_clusters,
#                             by = "mz_rt")
# dim(lyc_soy_effect) # how many features were removed?

Export venned list

write_csv(lyc_soy_effect,
          "Feature lists/sig-features-lyc-soy-effect.csv")

Mummichog matches

Mummichog makes a list of hits for all of the compounds, so we only need to do an inner join for one of the lists. The outcome would be the same for all of the lists used since I have to input my whole dataset (no cutoffs) into analysis.

mummichog_mass_matches_lycsoy <- inner_join(lyc_soy_effect,
                                            red_mummichog_hits,
                                            by = "mz_rt")

length(unique(mummichog_mass_matches_lycsoy$mz_rt)) # how many?
## [1] 3
tibble(unique(mummichog_mass_matches_lycsoy$mz_rt)) # which features have a hit with mummichog?
## # A tibble: 3 × 1
##   `unique(mummichog_mass_matches_lycsoy$mz_rt)`
##   <chr>                                        
## 1 469.0527_4.611                               
## 2 86.06_5.38                                   
## 3 153.1273_3.887
# map KEGG IDs to compound names
lycsoy_KEGGmatches <- as.data.frame(cpdkegg2name(mummichog_mass_matches_lycsoy$Matched.Compound))

mummichog_mass_matches_lycsoy$NAME <- lycsoy_KEGGmatches$NAME

Boxplots

all sig tomato features
# metabs with pval < 0.05 and fc >= 1.51
sigmetabs_lycsoy_effect <- lyc_soy_effect$mz_rt 
lycsoy_effect_df_for_stats_wide <- df_for_stats %>%
  dplyr::select(!rel_abund) %>%
  pivot_wider(names_from = mz_rt,
              values_from = rel_abund_log2) %>%
  dplyr::select(c(1:11),
                sigmetabs_lycsoy_effect)

# make tidy df
lycsoy_effect_df_for_stats_tidy <- lycsoy_effect_df_for_stats_wide %>%
  pivot_longer(cols = 12:ncol(.),
               names_to = "mz_rt",
               values_to = "rel_abund_log2")
# changing factor levels for pre_post_intervention
lycsoy_effect_df_for_stats_tidy$pre_post_intervention <- factor(lycsoy_effect_df_for_stats_tidy$pre_post_intervention,
                              levels = c("pre_Yellow", "post_Yellow", "pre_Red", "post_Red"))

levels(lycsoy_effect_df_for_stats_tidy$pre_post_intervention)  
## [1] "pre_Yellow"  "post_Yellow" "pre_Red"     "post_Red"
lycsoy_effect_df_for_stats_tidy %>% 
  ggplot(aes(x = pre_post_intervention, y = rel_abund_log2, fill = Intervention)) +
  geom_boxplot(outlier.shape = NA) +
  scale_fill_manual(values = c("Yellow" = "gold",
                                           "Red" = "tomato1")) +
  geom_line(aes(group = Subject), fill = "brown", linewidth = 0.2) +
  facet_wrap(vars(mz_rt), scales = "free_y") + 
  theme_clean() 

Low carotenoid tomato effect

  1. yellow tomato effect: use list that only keeps features that are both significant between pre and post-yellow, and also significant between post-Red and post-Yellow
# sig features from post-red vs post-yellow
dim(sig_postintervention_noOutlier)
## [1] 35 13
# select only sig features that are present when comparing pre-Yellow to post-Yellow
low_carot_tomato_effect <- inner_join(sig_postintervention_noOutlier,
                             yel_sig_prepost_comp_clusters,
                             by = "mz_rt") 
dim(low_carot_tomato_effect)
## [1] 12 25

Export venned list

write_csv(low_carot_tomato_effect,
          "Feature lists/sig-features-low-carot-tomato-effect.csv")

Mummichog masses

mummichog_mass_matches_yellow_tomato <- inner_join(low_carot_tomato_effect,
                                            red_mummichog_hits,
                                            by = "mz_rt")

tibble(unique(mummichog_mass_matches_yellow_tomato$mz_rt)) # which features have a hit with mummichog?
## # A tibble: 5 × 1
##   `unique(mummichog_mass_matches_yellow_tomato$mz_rt)`
##   <chr>                                               
## 1 104.0706_5.342                                      
## 2 146.0925_2.629                                      
## 3 86.06_5.38                                          
## 4 188.0705_0.471                                      
## 5 312.2284_1.053
length(unique(mummichog_mass_matches_yellow_tomato$mz_rt)) # how many?
## [1] 5
# map KEGG IDs to compound names
yellow_KEGGmatches <- as.data.frame(cpdkegg2name(mummichog_mass_matches_yellow_tomato$Matched.Compound))

mummichog_mass_matches_yellow_tomato$NAME <- yellow_KEGGmatches$NAME

Boxplots

all sig yellow tom features
# metabs with pval < 0.05 and fc >= 1.51
sigmetabs_yellow_effect <- as.character(low_carot_tomato_effect$mz_rt)
yellow_effect_df_for_stats_wide <- df_for_stats %>%
  dplyr::select(!rel_abund) %>%
  pivot_wider(names_from = mz_rt,
              values_from = rel_abund_log2) %>%
  dplyr::select(c(1:11),
                sigmetabs_yellow_effect)

# make tidy df
yellow_effect_df_for_stats_tidy <- yellow_effect_df_for_stats_wide %>%
  pivot_longer(cols = 12:ncol(.),
               names_to = "mz_rt",
               values_to = "rel_abund_log2")
# changing factor levels for pre_post_intervention
yellow_effect_df_for_stats_tidy$pre_post_intervention <- factor(yellow_effect_df_for_stats_tidy$pre_post_intervention,
                              levels = c("pre_Yellow", "post_Yellow", "pre_Red", "post_Red"))

levels(yellow_effect_df_for_stats_tidy$pre_post_intervention)  
## [1] "pre_Yellow"  "post_Yellow" "pre_Red"     "post_Red"
yellow_effect_df_for_stats_tidy %>% 
  ggplot(aes(x = pre_post_intervention, y = rel_abund_log2, fill = Intervention)) +
  geom_boxplot(outlier.shape = NA) +
  scale_fill_manual(values = c("Yellow" = "gold",
                                           "Red" = "tomato1")) +
  geom_line(aes(group = Subject), fill = "brown", linewidth = 0.2) +
  facet_wrap(vars(mz_rt), scales = "free_y") + 
  theme_clean() 

Immuno Correlations

IL-5, IL-12p70, and GM-CSF were significantly decreased after tomato-soy intervention (only when comparing pre to post in Red) according to Wilcoxon rank-sum tests (p<0.05). Immune cell types were significantly altered in all 3 comparisons - so let’s see how these significant immuno outcomes correlate with metabolites found to be significant at p > 0.05 and FC >=1.61.

Import other outcomes (carotenoids and immunology data)

# load data
carot_immunology_meta <- read_excel("CompiledData_Results_Meta.xlsx",
                         sheet = "metadata_corrected_withsequence")

# clean up variable names 
carot_immunology_meta <- clean_names(carot_immunology_meta)

Wrangle

# convert variables that should be factors to factors
carot_immunology_meta <- carot_immunology_meta %>%
  filter(intervention != "Baseline") %>%
  mutate(across(.cols = c("patient_id", "period", 
                          "intervention", "intervention_week", 
                          "pre_post", "sex", "sequence"),
                .fns = as.factor))


# some stuff came in as characters but should be numeric
carot_immunology_meta <- carot_immunology_meta %>%
  mutate(across(.cols = c("il_2", "il_10", "il_13", "il_4"),
                .fns = as.numeric))



# changing factor levels for pre_post
carot_immunology_meta$pre_post <- factor(carot_immunology_meta$pre_post,
                              levels = c("pre", "post"))

levels(carot_immunology_meta$pre_post)        
## [1] "pre"  "post"
# Calculate total_cis_lyc, total_lyc, and total_carotenoids
carot_immunology_meta <- carot_immunology_meta %>%
  mutate(total_cis_lyc = other_cis_lyc + x5_cis_lyc,
         total_lyc = all_trans_lyc + total_cis_lyc,
         total_carotenoids = lutein + zeaxanthin + b_cryptoxanthin + 
                             a_carotene + b_carotene + total_lyc)

Rm outliers + more wrangling

# go back to wide for stats df
df_for_stats_wide_noOutlier <- df_for_stats_noOutlier %>%
  pivot_wider(names_from = mz_rt,
              values_from = rel_abund_log2)

# take outliers out of carot_immuno df 
carot_immunology_meta_noOutlier <- carot_immunology_meta %>%
  filter(patient_id != 6106,
         patient_id != 6112)

# add sig immuno outcome columns to dataset
forcorr_metabslog2_immuno_noOutlier <- df_for_stats_wide_noOutlier %>%
  mutate(il_5 = carot_immunology_meta_noOutlier$il_5,
         il_12p70 = carot_immunology_meta_noOutlier$il_12p70,
         gm_csf = carot_immunology_meta_noOutlier$gm_csf,
         total_lyc = carot_immunology_meta_noOutlier$total_lyc,
         # sig immune cells pre vs. post red
         CD45ROpos_CD45RAneg_EMCD8 = carot_immunology_meta_noOutlier$x08_cd45ro_cd45ra_em_cd8,
         CD19posCD3neg_Bcells = carot_immunology_meta_noOutlier$x25_cd19_cd3_b_cells,
         CD27neg_IgDpos_naiveBcells = carot_immunology_meta_noOutlier$x26_cd27_ig_d_naive_b_cells,
         # sig immune cells post-yellow vs. post red
         CD16negNKcells = carot_immunology_meta_noOutlier$x38_cd16_nk_cells,
         CD14posMDSCs = carot_immunology_meta_noOutlier$x40_cd14_mdsc_mono)

Here, I am going to look at correlations between fold change differences and sig metabolites from the lycopene/soy effect list

More wrangling

No outliers

Subset df’s into pre and post

# pre
forcorr_pre_metabslog2_immuno_noOutlier <- forcorr_metabslog2_immuno_noOutlier %>%
  dplyr::select(Subject, Intervention, pre_post, 12:ncol(.)) %>%
  filter(pre_post == "pre")

# post 
forcorr_post_metabslog2_immuno_noOutlier <- forcorr_metabslog2_immuno_noOutlier %>%
  dplyr::select(Subject, Intervention, pre_post, 12:ncol(.)) %>%
  filter(pre_post == "post")

Create df with differences calculated for each outcome

# difference for calculated for each outcome
forcorr_diff_red_metabslog2_immuno_noOutlier <- forcorr_post_metabslog2_immuno_noOutlier[,-c(1:3)] - forcorr_pre_metabslog2_immuno_noOutlier[,-c(1:3)]

Lycsoy metab list Corr

These are correlations based on significant metabolites with FDR < 0.05 AND FC > 1.51 (from lyc/soy effect list) against immuno outcomes that were significant (padj < 0.05) when comparing pre to post red intervention.

# add subject and intervention back to df. Select only significant metabolites (pval > 0.05 and FC >=1.51) that are in lyc/soy effect list
lyc_soy_forcorr_diff_red_metabslog2_immuno_noOutlier <- forcorr_diff_red_metabslog2_immuno_noOutlier %>%
  mutate(Subject = forcorr_post_metabslog2_immuno_noOutlier$Subject,
         Intervention = forcorr_post_metabslog2_immuno_noOutlier$Intervention) %>%
  filter(Intervention == "Red") %>%
  dplyr::select(Subject, Intervention, il_5, il_12p70, gm_csf, total_lyc, CD45ROpos_CD45RAneg_EMCD8, CD19posCD3neg_Bcells, CD27neg_IgDpos_naiveBcells, CD16negNKcells, CD14posMDSCs, all_of(lyc_soy_effect$mz_rt))

# correlation table
lycsoy_red_corr_results_metabs_immuno_diff_noOutlier <- lyc_soy_forcorr_diff_red_metabslog2_immuno_noOutlier %>% 
  correlate(method = "spearman") %>%
  rearrange() %>%
  shave()

kable(lycsoy_red_corr_results_metabs_immuno_diff_noOutlier, format = "markdown", digits = 3)
term 540.4236_0.454 il_12p70 il_5 CD45ROpos_CD45RAneg_EMCD8 229.1553_4.5 gm_csf CD14posMDSCs CD16negNKcells CD19posCD3neg_Bcells 208.0968_0.848 CD27neg_IgDpos_naiveBcells 86.06_5.38 447.0919_4.001 total_lyc 433.2365_1.214 234.1136_0.825 433.1123_4.289 449.1074_3.962 259.0977_3.914 607.1285_8.059 303.1914_5.064 153.1273_3.887 431.0968_4.511 461.1072_4.293 469.0527_4.611
540.4236_0.454 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
il_12p70 0.527 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
il_5 0.685 0.648 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
CD45ROpos_CD45RAneg_EMCD8 0.321 0.733 0.164 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
229.1553_4.5 0.661 0.139 0.406 0.091 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
gm_csf 0.067 0.503 -0.018 0.600 0.309 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
CD14posMDSCs 0.370 -0.067 0.273 -0.115 0.539 0.139 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
CD16negNKcells 0.430 0.406 0.648 0.030 -0.164 -0.321 0.297 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
CD19posCD3neg_Bcells -0.018 0.588 0.224 0.503 -0.370 0.152 -0.188 0.455 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
208.0968_0.848 0.188 0.467 0.515 0.176 -0.200 0.139 0.394 0.782 0.321 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
CD27neg_IgDpos_naiveBcells -0.115 0.479 0.079 0.467 -0.394 0.164 -0.285 0.309 0.976 0.164 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
86.06_5.38 -0.127 -0.127 -0.164 0.261 0.358 0.491 0.224 -0.345 -0.188 -0.018 -0.127 NA NA NA NA NA NA NA NA NA NA NA NA NA NA
447.0919_4.001 -0.018 0.394 0.042 0.176 0.042 0.648 0.236 -0.115 0.067 0.285 0.006 -0.200 NA NA NA NA NA NA NA NA NA NA NA NA NA
total_lyc -0.055 -0.042 0.030 -0.030 -0.188 0.067 0.236 0.382 0.382 0.370 0.442 0.224 -0.115 NA NA NA NA NA NA NA NA NA NA NA NA
433.2365_1.214 0.091 0.406 0.164 -0.030 0.079 0.406 -0.370 -0.164 0.067 -0.091 0.103 -0.273 0.503 -0.103 NA NA NA NA NA NA NA NA NA NA NA
234.1136_0.825 -0.212 -0.297 -0.006 -0.261 0.079 -0.055 -0.006 -0.358 -0.224 -0.297 -0.139 0.018 0.079 0.224 0.030 NA NA NA NA NA NA NA NA NA NA
433.1123_4.289 -0.358 -0.309 -0.152 -0.418 0.188 0.188 -0.285 -0.612 -0.503 -0.394 -0.394 0.333 0.042 -0.127 0.503 0.430 NA NA NA NA NA NA NA NA NA
449.1074_3.962 -0.467 -0.152 -0.442 -0.248 -0.624 0.030 0.042 0.176 0.297 0.285 0.309 -0.345 0.418 0.345 0.139 -0.224 -0.224 NA NA NA NA NA NA NA NA
259.0977_3.914 -0.309 -0.394 -0.042 -0.370 -0.491 -0.818 -0.479 0.200 -0.018 -0.103 0.006 -0.152 -0.794 -0.006 -0.370 0.030 0.030 -0.164 NA NA NA NA NA NA NA
607.1285_8.059 -0.661 -0.067 -0.261 -0.212 -0.358 0.188 -0.600 -0.479 0.127 -0.309 0.236 -0.067 0.261 -0.030 0.600 0.406 0.685 0.212 0.067 NA NA NA NA NA NA
303.1914_5.064 -0.430 -0.358 -0.358 -0.430 -0.806 -0.479 -0.345 0.297 0.091 0.200 0.139 -0.358 -0.212 0.418 -0.006 -0.091 -0.079 0.648 0.515 0.164 NA NA NA NA NA
153.1273_3.887 -0.176 -0.515 -0.309 -0.539 -0.442 -0.794 -0.527 0.018 -0.248 -0.370 -0.176 -0.467 -0.576 -0.091 -0.030 -0.030 0.067 0.103 0.733 0.067 0.685 NA NA NA NA
431.0968_4.511 -0.685 -0.358 -0.697 -0.127 -0.539 0.091 -0.297 -0.467 -0.333 -0.079 -0.309 -0.006 0.309 -0.370 -0.018 -0.091 0.200 0.430 0.030 0.321 0.333 0.164 NA NA NA
461.1072_4.293 -0.515 -0.467 -0.564 -0.539 -0.455 -0.067 -0.030 -0.224 -0.176 -0.103 -0.103 -0.394 0.442 0.188 0.261 0.273 0.176 0.782 -0.152 0.406 0.576 0.285 0.527 NA NA
469.0527_4.611 -0.515 -0.503 -0.297 -0.648 -0.382 -0.236 0.006 -0.176 -0.539 0.091 -0.552 -0.273 0.333 -0.164 0.067 0.321 0.358 0.394 0.127 0.321 0.430 0.273 0.685 0.709 NA

Look for strongly correlated outcomes. R^2 >= to 0.5

lycsoy_red_sigcorr_metabsP05_FC1.6_immunodata <- lycsoy_red_corr_results_metabs_immuno_diff_noOutlier %>%
  dplyr::select(term, il_5, il_12p70, gm_csf, total_lyc, CD45ROpos_CD45RAneg_EMCD8, CD19posCD3neg_Bcells, CD27neg_IgDpos_naiveBcells) %>%
  filter(abs(lycsoy_red_corr_results_metabs_immuno_diff_noOutlier$il_5) >= 0.5 |
           abs(lycsoy_red_corr_results_metabs_immuno_diff_noOutlier$il_12p70) >= 0.5 |
           abs(lycsoy_red_corr_results_metabs_immuno_diff_noOutlier$gm_csf) >= 0.5 |
           abs(lycsoy_red_corr_results_metabs_immuno_diff_noOutlier$total_lyc) >= 0.5 |
           abs(lycsoy_red_corr_results_metabs_immuno_diff_noOutlier$CD45ROpos_CD45RAneg_EMCD8) >= 0.5 |
           abs(lycsoy_red_corr_results_metabs_immuno_diff_noOutlier$CD19posCD3neg_Bcells) >= 0.5 |
           abs(lycsoy_red_corr_results_metabs_immuno_diff_noOutlier$CD27neg_IgDpos_naiveBcells) >= 0.5 |
           abs(lycsoy_red_corr_results_metabs_immuno_diff_noOutlier$CD16negNKcells) >= 0.5 |
           abs(lycsoy_red_corr_results_metabs_immuno_diff_noOutlier$CD14posMDSCs) >= 0.5) 

kable(lycsoy_red_sigcorr_metabsP05_FC1.6_immunodata, digits = 3, caption = "sig metabolites in lyc/soy effect list and sig immuno outcomes with strong correlations. R^2 >= to 0.5. Based on Pre vs. post-red log2fc")
sig metabolites in lyc/soy effect list and sig immuno outcomes with strong correlations. R^2 >= to 0.5. Based on Pre vs. post-red log2fc
term il_5 il_12p70 gm_csf total_lyc CD45ROpos_CD45RAneg_EMCD8 CD19posCD3neg_Bcells CD27neg_IgDpos_naiveBcells
il_5 NA 0.648 NA NA NA NA NA
CD45ROpos_CD45RAneg_EMCD8 0.164 0.733 NA NA NA NA NA
gm_csf -0.018 0.503 NA NA 0.600 NA NA
CD16negNKcells 0.648 0.406 -0.321 NA 0.030 NA NA
CD19posCD3neg_Bcells 0.224 0.588 0.152 NA 0.503 NA NA
208.0968_0.848 0.515 0.467 0.139 NA 0.176 0.321 NA
CD27neg_IgDpos_naiveBcells 0.079 0.479 0.164 NA 0.467 0.976 NA
447.0919_4.001 0.042 0.394 0.648 NA 0.176 0.067 0.006
433.1123_4.289 -0.152 -0.309 0.188 -0.127 -0.418 -0.503 -0.394
259.0977_3.914 -0.042 -0.394 -0.818 -0.006 -0.370 -0.018 0.006
607.1285_8.059 -0.261 -0.067 0.188 -0.030 -0.212 0.127 0.236
153.1273_3.887 -0.309 -0.515 -0.794 -0.091 -0.539 -0.248 -0.176
431.0968_4.511 -0.697 -0.358 0.091 -0.370 -0.127 -0.333 -0.309
461.1072_4.293 -0.564 -0.467 -0.067 0.188 -0.539 -0.176 -0.103
469.0527_4.611 -0.297 -0.503 -0.236 -0.164 -0.648 -0.539 -0.552

plot

lycsoy_red_ggcorr_noOutlier <- round(cor(lyc_soy_forcorr_diff_red_metabslog2_immuno_noOutlier[,-c(1:2)], method = "spearman"), digits = 2)


(lyc_soy_immuno_metabs_ggcorrplot <- lycsoy_red_ggcorr_noOutlier %>%
  ggcorrplot(lab = TRUE, 
             outline.color = "white", type = "full", hc.order = FALSE,
             ggtheme = ggthemes::theme_clean(base_size = 8, base_family = "sans"),
  colors = c("#6D9EC1", "white", "#E46726")) +
  theme(axis.text.x = element_text(angle = 90, size = 12),
        axis.text.y = element_text(size = 12)))

Export corplot

ggsave(plot = lyc_soy_immuno_metabs_ggcorrplot,
       filename = "plots and figures/prepostRedFC-HILIC-pos-lycsoy-Metabs-and-immunology-corrplot.svg",
       width = 14,
       height = 14)
LS0tCnRpdGxlOiAiVVNEQSBJbmZsYW1tYXRpb24gTWV0YWJvbG9taWNzIERhdGEgYW5hbHlzaXMiCnN1YnRpdGxlOiAiVXJpbmUsIEhJTElDKCspIExDTVMiCmF1dGhvcjogIk1hcmlhIFNob2xvbGEiCmRhdGU6ICcyMDIzLTA2LTA1JwpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICBoaWdobGlnaHQ6IGthdGUKICAgIHRoZW1lOiB5ZXRpCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2RlcHRoOiA1CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBmaWdfd2lkdGg6IDcKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0gCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZWNobz1UUlVFKSAKYGBgCgojIExvYWQgbGlicmFyaWVzCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpICMgZm9yIGV2ZXJ5dGhpbmcKbGlicmFyeShyZWFkeGwpICMgZm9yIHJlYWRpbmcgaW4gZXhjZWwgZmlsZXMKbGlicmFyeShqYW5pdG9yKSAjIGRhdGEgY2hlY2tzIGFuZCBjbGVhbmluZwpsaWJyYXJ5KGdsdWUpICMgZm9yIGVhc3kgcGFzdGluZwpsaWJyYXJ5KEZhY3RvTWluZVIpICMgZm9yIFBDQQpsaWJyYXJ5KGZhY3RvZXh0cmEpICMgZm9yIFBDQQpsaWJyYXJ5KHJzdGF0aXgpICMgZm9yIHN0YXRzCmxpYnJhcnkocGhlYXRtYXApICMgZm9yIGhlYXRtYXBzCmxpYnJhcnkocGxvdGx5KSAjIGZvciBpbnRlcmFjdGl2ZSBwbG90cwpsaWJyYXJ5KGh0bWx3aWRnZXRzKSAjIGZvciBzYXZpbmcgaW50ZXJhY3RpdmUgcGxvdHMKbGlicmFyeShkZXZ0b29scykKbGlicmFyeShub3RhbWUpICMgdXNlZCBmb3IgZmVhdHVyZSBjbHVzdGVyaW5nCmxpYnJhcnkoZG9QYXJhbGxlbCkKbGlicmFyeShpZ3JhcGgpICMgZmVhdHVyZSBjbHVzdGVyaW5nCmxpYnJhcnkoZ2dwdWJyKSAjIHZpc3VhbGl6YXRpb25zCmxpYnJhcnkoa25pdHIpICMgY2xlYW4gdGFibGUgcHJpbnRpbmcKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkobWl4T21pY3MpICMgZm9yIG11bHRpbGV2ZWwgUENBcwpsaWJyYXJ5KHBhdGh2aWV3KSAjIGZvciBmdW5jdGlvbmFsIGFuYWx5c2lzIGFuZCBLRUdHIGFubm90YXRpb24KbGlicmFyeShnZ2NvcnJwbG90KQpsaWJyYXJ5KGNvcnJyKQpsaWJyYXJ5KGdndGhlbWVzKQpgYGAKCiMgUmVhZCBpbiBkYXRhCmBgYHtyfQojIHJhdyBmaWx0ZXJlZCBtZXRhYm9sb21pY3MgZGF0YSBpbiBISUxJQyAoKykKb21pY3NkYXRhIDwtIHJlYWRfY3N2KCJGZWF0dXJlIGxpc3RzL0hJTElDLVBPUy1GaWx0ZXJlZC1EYXRhLTA1SnVuMjNfMTQ0NGZlYXR1cmVzLmNzdiIpCgojIG1ldGFkYXRhCm1ldGFkYXRhIDwtIHJlYWRfZXhjZWwoIk1ldGFkYXRhLXVyaW5lLUhJTElDLVBPUy54bHN4IikKYGBgCgojIFdyYW5nbGUgZGF0YQoKYGBge3J9Cm1ldGFkYXRhIDwtIG1ldGFkYXRhICU+JQogIHJlbmFtZSgic2FtcGxlX0lEIiA9IFNhbXBsZV9JRCkKYGBgCgoKYGBge3J9CiMgcmVuYW1lICJyb3cgSUQiCm9taWNzZGF0YSA8LSBvbWljc2RhdGEgJT4lCiAgcmVuYW1lKCJyb3dfSUQiID0gYHJvdyBJRGApCgojIGhvdyBtYW55IGZlYXR1cmVzCm5yb3cob21pY3NkYXRhKQoKIyBhcmUgdGhlcmUgYW55IGR1cGxpY2F0ZXM/Cm9taWNzZGF0YSAlPiUgZ2V0X2R1cGVzKG16X3J0KQoKYGBgCgpgYGB7cn0KIyByZW1vdmUgZHVwZXMKb21pY3NkYXRhIDwtIG9taWNzZGF0YSAlPiUgCiAgZGlzdGluY3QobXpfcnQsIC5rZWVwX2FsbCA9IFRSVUUpCgojIGNoZWNrIGFnYWluIGZvciBkdXBlcwpvbWljc2RhdGEgJT4lIGdldF9kdXBlcyhtel9ydCkKCiMgaG93IG1hbnkgZmVhdHVyZXMKbnJvdyhvbWljc2RhdGEpCmBgYApTb21ldGltZXMgYSB3ZWlyZCBsb2dpY2FsIGNvbHVtbiAobGdsKSBjb21lcyB1cCBpbiBteSBkYXRhLiBMZXQncyBjaGVjayBpZiBpdCdzIHRoZXJlCgpgYGB7cn0KY29sbmFtZXMob21pY3NkYXRhKQpgYGAKCgpgYGB7cn0KIyByZW1vdmUgd2VpcmQgbGdsIGNvbHVtbgpvbWljc2RhdGEgPC0gb21pY3NkYXRhICU+JQogIGRwbHlyOjpzZWxlY3QoIXdoZXJlKGlzLmxvZ2ljYWwpKQoKY29sbmFtZXMob21pY3NkYXRhKQpgYGAKCgoKYGBge3J9CiMgY3JlYXRlIGxvbmcgZGYgZm9yIG9taWNzIGRmCm9taWNzZGF0YV90aWR5IDwtIG9taWNzZGF0YSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IDM6bmNvbCguKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAic2FtcGxlX0lEIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInBlYWtfaGVpZ2h0IikKCiMgY29tYmluZSBtZXRhIGFuZCBvbWljcyBkZnMKZGZfY29tYmluZWQgPC0gZnVsbF9qb2luKG9taWNzZGF0YV90aWR5LAogICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoInNhbXBsZV9JRCIgPSAic2FtcGxlX0lEIikpCgojIHNlcGFyYXRlIG16IGFuZCBydApkZl9jb21iaW5lZF9zZXAgPC0gZGZfY29tYmluZWQgJT4lCiAgc2VwYXJhdGUoY29sID0gbXpfcnQsCiAgICAgICAgICAgaW50byA9IGMoIm16IiwgInJ0IiksCiAgICAgICAgICAgc2VwID0gIl8iKSAKCiMgY29udmVydCBjb2x1bW5zIHRvIGNvcnJlY3QgdHlwZQpkZl9jb21iaW5lZF9zZXAkbXogPC0gYXMubnVtZXJpYyhkZl9jb21iaW5lZF9zZXAkbXopCmRmX2NvbWJpbmVkX3NlcCRydCA8LSBhcy5udW1lcmljKGRmX2NvbWJpbmVkX3NlcCRydCkKZGZfY29tYmluZWRfc2VwJFN1YmplY3QgPC0gYXMuY2hhcmFjdGVyKGRmX2NvbWJpbmVkX3NlcCRTdWJqZWN0KQpkZl9jb21iaW5lZF9zZXAkSW50ZXJ2ZW50aW9uIDwtIGFzLmNoYXJhY3RlcihkZl9jb21iaW5lZF9zZXAkSW50ZXJ2ZW50aW9uKQoKIyByZWFycmFuZ2UgY29sdW1uIG9yZGVyCmRmX2NvbWJpbmVkX3NlcCA8LSBkZl9jb21iaW5lZF9zZXAgJT4lCiAgZHBseXI6OnNlbGVjdChzYW1wbGVfSUQsIHByZV9wb3N0LCBJbnRlcnZlbnRpb24sIGV2ZXJ5dGhpbmcoKSkKCnN0cihkZl9jb21iaW5lZF9zZXApCgojIHJlcGxhY2UgTkEncyBpbiBzdWJqZWN0IGFuZCBpbnRlcnZlbnRpb24gY29sdW1ucyB3aXRoIFFDCmRmX2NvbWJpbmVkX3NlcCRTdWJqZWN0IDwtIGRmX2NvbWJpbmVkX3NlcCRTdWJqZWN0ICU+JQogIHJlcGxhY2VfbmEoIlFDIikKCmRmX2NvbWJpbmVkX3NlcCRJbnRlcnZlbnRpb24gPC0gZGZfY29tYmluZWRfc2VwJEludGVydmVudGlvbiAlPiUKICByZXBsYWNlX25hKCJRQyIpCgoKYGBgCgojIERhdGEgc3VtbWFyaWVzCgojIyBOdW1iZXIgb2YgbWFzc2VzIGRldGVjdGVkCmBgYHtyfQpucm93KG9taWNzZGF0YSkKYGBgCgoKIyMgTWFzcyByYW5nZSBmb3IgbWV0YWJvbGl0ZXMgZGV0ZWN0ZWQ/CgpgYGB7cn0KcmFuZ2UoZGZfY29tYmluZWRfc2VwJG16KQpgYGAKCiMjIFJUIHJhbmdlIGZvciBtZXRhYm9saXRlcyBkZXRlY3RlZD8KCmBgYHtyfQpyYW5nZShkZl9jb21iaW5lZF9zZXAkcnQpCmBgYAoKIyMgbWFzcyB2cyBSVCBzY2F0dGVycGxvdApgYGB7cn0KIyBwbG90CihwbG90X216dnNydCA8LSBkZl9jb21iaW5lZF9zZXAgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcnQsIHkgPSBteikpICsKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh4ID0gIlJldGVudGlvbiB0aW1lLCBtaW4iLAogICAgICAgeSA9ICJtL3osIG5ldXRyYWwiLAogICAgICAgdGl0bGUgPSAibXogYWNyb3NzIFJUIGZvciBhbGwgZmVhdHVyZXMiKSkKYGBgCgojIyBIaXN0b2dyYW0gZm9yIG1hc3MgcmFuZ2UKYGBge3J9CmRmX2NvbWJpbmVkX3NlcCAlPiUKICBnZ3Bsb3QoYWVzKHggPSBteikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDI1KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHggPSAiTW9ub2lzb3RvcGljIG1hc3MgKGFtdSkiLAogICAgICAgeSA9ICJOdW1iZXIgb2YgZmVhdHVyZXMiLAogICAgICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGZlYXR1cmVzIGJ5IG1hc3MiKQpgYGAKCiMjIEhpc3RvZ3JhbSBmb3IgUlQKCmBgYHtyfQpkZl9jb21iaW5lZF9zZXAgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcnQpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEpICsgIyA2IHNlY29uZCBiaW5zCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHggPSAiUmV0ZW50aW9uIHRpbWUiLAogICAgICAgeSA9ICJOdW1iZXIgb2YgZmVhdHVyZXMiLAogICAgICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGZlYXR1cmVzIGJ5IHJldGVudGlvbiB0aW1lIikKYGBgCgoKIyBOQXMgYW5kIGltcHV0aW5nCgojIyBOQXMKYGBge3J9CiMgTkFzIGluIGFsbCBkYXRhIGluY2x1ZGluZyBRQ3MKTkFieVJvdyA8LSByb3dTdW1zKGlzLm5hKG9taWNzZGF0YVssLTFdKSkKCmhpc3QoTkFieVJvdywKICAgICBicmVha3MgPSA1NiwgIyBiZWNhdXNlIHRoZXJlIGFyZSA1NiBzYW1wbGVzLCA0OCBzYW1wbGVzICsgOCBRQ3MKICAgICB4bGFiID0gIk51bWJlciBvZiBtaXNzaW5nIHZhbHVlcyIsCiAgICAgeWxhYiA9ICJOdW1iZXIgb2YgbWV0YWJvbGl0ZXMiLAogICAgIG1haW4gPSAiSG93IG1hbnkgbWlzc2luZyB2YWx1ZXMgYXJlIHRoZXJlPyIpCmBgYAoKYGBge3J9CiMgc2FtcGxlcyBvbmx5IChubyBRQ3MpCm9taWNzZGF0YV9ub1FDIDwtIG9taWNzZGF0YSAlPiUKICBkcGx5cjo6c2VsZWN0KC1jb250YWlucygiUUMiKSkKCiNOQXMgaW4gc2FtcGxlcyBvbmx5PwpOQWJ5Um93X25vUUMgPC0gcm93U3Vtcyhpcy5uYShvbWljc2RhdGFfbm9RQ1ssLTFdKSkKCmhpc3QoTkFieVJvd19ub1FDLAogICAgIGJyZWFrcyA9IDQ4LCAjIGJlY2F1c2UgdGhlcmUgYXJlIDQ4IHNhbXBsZXMgCiAgICAgeGxhYiA9ICJOdW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMiLAogICAgIHlsYWIgPSAiTnVtYmVyIG9mIG1ldGFib2xpdGVzIiwKICAgICBtYWluID0gIkhvdyBtYW55IG1pc3NpbmcgdmFsdWVzIGFyZSB0aGVyZT8iKQpgYGAKCkFyZSB0aGVyZSBhbnkgbWlzc2luZyB2YWx1ZXMgaW4gUUNzPyBUaGVyZSBzaG91bGRuJ3QgYmUgYWZ0ZXIgZGF0YSBwcmVwcm9jZXNzaW5nL2ZpbHRlcmluZwpgYGB7cn0Kb21pY3NkYXRhX1FDIDwtIG9taWNzZGF0YSAlPiUKICBkcGx5cjo6c2VsZWN0KHN0YXJ0c193aXRoKCJQIikpIAoKTkFieVJvd19RQyA8LSBjb2xTdW1zKGlzLm5hKG9taWNzZGF0YV9RQykpCiMgbGV0cyBjb25maXJtIHRoYXQgdGhlcmUgYXJlIG5vIG1pc3NpbmcgdmFsdWVzIGZyb20gbXkgUUNzCnN1bShOQWJ5Um93X1FDKSAjIG5vCmBgYAoKCmBgYHtyfQojIGNhbGN1bGF0ZSBob3cgbWFueSBOQXMgdGhlcmUgYXJlIHBlciBmZWF0dXJlIGluIHdob2xlIGRhdGEgc2V0CmNvbnRhaW5zX05BcyA8LSBkZl9jb21iaW5lZCAlPiUKICBncm91cF9ieShtel9ydCkgJT4lCiAgY291bnQoaXMubmEocGVha19oZWlnaHQpKSAlPiUKICBmaWx0ZXIoYGlzLm5hKHBlYWtfaGVpZ2h0KWAgPT0gVFJVRSkKaGVhZChjb250YWluc19OQXMpCmBgYAoKCgoKIyMgUmVtb3ZlIE5BcwoKVXBkYXRlIE1heSAzLCAyMDIzOiBJJ3ZlIGJlZW4gc2VlaW5nIG91dGxpZXJzIGluIHVuc3VwZXJ2aXNlZCBhbmFseXNlcy4gU28gdG8gaGFuZGxlIHRoaXMsIEkgdGhpbmsgaXQgaXMgYmVzdCB0byBmaWx0ZXIgb3V0IG1ldGFib2xpdGVzIHRoYXQgYXJlIG9ubHkgcHJlc2VudCBpbiBvbmUgcGVyc29uLiBTbyBJIHdpbGwgcmVtb3ZlIG1ldGFib2xpdGVzIHRoYXQgYXJlIG1pc3NpbmcgZnJvbSBhdCBsZWFzdCA0NCBzYW1wbGVzLiBUYWtpbmcgdGhpcyBvdXQgZm9yIG5vdy4gCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyByZW1vdmUgZmVhdHVyZXMgdGhhdCBoYXZlIDQ0IG9yIG1vcmUgTkFzCm9taXRfZmVhdHVyZXMgPC0gY29udGFpbnNfTkFzICU+JQogIGZpbHRlcihuID49IDQ0KQojcHJldmlldwpucm93KG9taXRfZmVhdHVyZXMpICMgMTA5IGZlYXR1cmVzIHRvIHJlbW92ZQoKIyBob3cgbWFueSBmZWF0dXJlcyB0byByZW1vdmU/Cm5yb3cob21pY3NkYXRhKSAtIG5yb3cob21pdF9mZWF0dXJlcykKCiMgbm93IHJlbW92ZSB0aGVzZSBmZWF0dXJlcyBmcm9tIHRoZSBvbWljcyBkYXRhc2V0Cm9taWNzZGF0YSA8LSBvbWljc2RhdGEgJT4lCiAgYW50aV9qb2luKG9taXRfZmVhdHVyZXMsCiAgICAgICAgICAgIGJ5ID0gIm16X3J0IikKCiAjIGhvdyBtYW55IGZlYXR1cmVzIGFyZSB0aGVyZSBub3c/Cm5yb3cob21pY3NkYXRhKQpgYGAKCgojIyBEYXRhIGltcHV0YXRpb24KYGBge3J9CiMgaW1wdXRlIGFueSBtaXNzaW5nIHZhbHVlcyBieSByZXBsYWNpbmcgdGhlbSB3aXRoIDEvMiBvZiB0aGUgbG93ZXN0IHBlYWsgaGVpZ2h0IHZhbHVlIG9mIGEgZmVhdHVyZSAoaS5lLiBpbiBhIHJvdykuCmltcHV0ZWRfb21pY3NkYXRhIDwtIG9taWNzZGF0YQoKaW1wdXRlZF9vbWljc2RhdGFbXSA8LSBsYXBwbHkoaW1wdXRlZF9vbWljc2RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSBpZmVsc2UoaXMubmEoeCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4oeCwgbmEucm0gPSBUUlVFKS8yLCB4KSkKCmRpbShpbXB1dGVkX29taWNzZGF0YSkKYGBgCgpBcmUgdGhlcmUgYW55IE5Bcz8KYGBge3J9CmltcHV0ZWRfb21pY3NkYXRhICU+JQogIGlzLm5hKCkgJT4lCiAgc3VtKCkKCiMgaW1wdXRhdGlvbnMgd29ya2VkCmBgYAoKCiMgQ3JlYXRlIG5ldyBpbXB1dGVkIHRpZHkgZGF0YXNldHMKYGBge3J9CiMgY3JlYXRlIGxvbmcgZGYgZm9yIGltcHV0ZWQgb21pY3MgZGYKaW1wdXRlZF9vbWljc2RhdGFfdGlkeSA8LSBpbXB1dGVkX29taWNzZGF0YSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IDM6bmNvbCguKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAic2FtcGxlX0lEIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInBlYWtfaGVpZ2h0IikKCiMgY29tYmluZSBtZXRhIGFuZCBpbXB1dGVkIG9taWNzIGRmcwppbXB1dGVkX2Z1bGxkYXRhIDwtIGZ1bGxfam9pbihpbXB1dGVkX29taWNzZGF0YV90aWR5LAogICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoInNhbXBsZV9JRCIgPSAic2FtcGxlX0lEIikpCgojIHNlcGFyYXRlIG16IGFuZCBydAppbXB1dGVkX2Z1bGxkYXRhX3NlcCA8LSBpbXB1dGVkX2Z1bGxkYXRhICU+JQogIHNlcGFyYXRlKGNvbCA9IG16X3J0LAogICAgICAgICAgIGludG8gPSBjKCJteiIsICJydCIpLAogICAgICAgICAgIHNlcCA9ICJfIikgCgojIGNvbnZlcnQgY29sdW1ucyB0byBjb3JyZWN0IHR5cGUKaW1wdXRlZF9mdWxsZGF0YV9zZXAkbXogPC0gYXMubnVtZXJpYyhpbXB1dGVkX2Z1bGxkYXRhX3NlcCRteikKaW1wdXRlZF9mdWxsZGF0YV9zZXAkcnQgPC0gYXMubnVtZXJpYyhpbXB1dGVkX2Z1bGxkYXRhX3NlcCRydCkKaW1wdXRlZF9mdWxsZGF0YV9zZXAkU3ViamVjdCA8LSBhcy5jaGFyYWN0ZXIoaW1wdXRlZF9mdWxsZGF0YV9zZXAkU3ViamVjdCkKaW1wdXRlZF9mdWxsZGF0YV9zZXAkSW50ZXJ2ZW50aW9uIDwtIGFzLmNoYXJhY3RlcihpbXB1dGVkX2Z1bGxkYXRhX3NlcCRJbnRlcnZlbnRpb24pCmBgYAoKCiMgTm90YW1lIGZlYXR1cmUgcmVkdWN0aW9uCnZpZ25ldHRlIGZvciByZWZlcmVuY2UKYGBge3J9CiNicm93c2VWaWduZXR0ZXMoIm5vdGFtZSIpCmBgYAoKIyMgUGxvdCBmZWF0dXJlcy4gUlQgdnMgbXogYmVmb3JlIG5vdGFtZQpgYGB7cn0KIyBydCB2cyBteiBwbG90CmltcHV0ZWRfZnVsbGRhdGFfc2VwICU+JQogIGdncGxvdChhZXMoeCA9IHJ0LCB5ID0gbXopKSArCiAgZ2VvbV9wb2ludCgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoeCA9ICJSVCAobWluKSIsCiAgICAgICB5ID0gIm16IikKYGBgCgoKIyMgRGF0YSByZXN0cnVjdHVyaW5nIGZvciBub3RhbWUKYGBge3J9CiMgY3JlYXRlIGZlYXR1cmVzIGxpc3QgZnJvbSBpbXB1dGVkIGRhdGEgc2V0IHRvIG9ubHkgaW5jbHVkZSB1bmlxdWUgZmVhdHVyZSBJRCdzIChtel9ydCksIG16IGFuZCBSVApmZWF0dXJlcyA8LSBpbXB1dGVkX2Z1bGxkYXRhX3NlcCAlPiUKICBjYmluZChpbXB1dGVkX2Z1bGxkYXRhJG16X3J0KSAlPiUKICByZW5hbWUoIm16X3J0IiA9ICJpbXB1dGVkX2Z1bGxkYXRhJG16X3J0IikgJT4lCiAgZHBseXI6OnNlbGVjdChjKG16X3J0LCBteiwgcnQpKSAlPiUKICBkaXN0aW5jdCgpICMgcmVtb3ZlIHRoZSBkdXBsaWNhdGUgcm93cwoKIyBjcmVhdGUgYSBzZWNvbmQgZGF0YSBmcmFtZSB3aGljaCBpcyBqdXN0IGltcHV0ZWRfZnVsbGRhdGEgcmVzdHJ1Y3R1cmVkIHRvIGFub3RoZXIgd2lkZSBmb3JtYXQKZGF0YV9ub3RhbWUgPC0gZGF0YS5mcmFtZShpbXB1dGVkX29taWNzZGF0YSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoLXJvd19JRCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0KCkpCgpkYXRhX25vdGFtZSA8LSBkYXRhX25vdGFtZSAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigpICU+JSAjIGNoYW5nZSBzYW1wbGVzIGZyb20gcm93bmFtZXMgdG8gaXRzIG93biBjb2x1bW4KICByb3dfdG9fbmFtZXMocm93X251bWJlciA9IDEpICMgY2hhbmdlIHRoZSBmZWF0dXJlIElEcyAobXpfcnQpIGZyb20gZmlyc3Qgcm93IG9icyBpbnRvIGNvbHVtbiBuYW1lcwoKCmBgYAoKQ2hlY2sgc3RydWN0dXJlcwpgYGB7cn0KIyBjaGVjayBpZiBteiBhbmQgcnQgYXJlIG51bWVyaWMKc3RyKGZlYXR1cmVzKQp0aWJibGUoZmVhdHVyZXMpCmBgYAoKYGBge3J9CiMgY2hlY2sgaWYgcmVzdWx0cyBhcmUgbnVtZXJpYwp0aWJibGUoZGF0YV9ub3RhbWUpCgojIGNoYW5nZSB0byByZXN1bHRzIHRvIG51bWVyaWMKZGF0YV9ub3RhbWUgPC0gZGF0YV9ub3RhbWUgJT4lCiAgbXV0YXRlX2F0KC0xLCBhcy5udW1lcmljKQoKdGliYmxlKGRhdGFfbm90YW1lKQpgYGAKCgojIyBGaW5kIGNvbm5lY3Rpb25zCmBgYHtyfQpjb25uZWN0aW9uIDwtIGZpbmRfY29ubmVjdGlvbnMoZGF0YSA9IGRhdGFfbm90YW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBmZWF0dXJlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcnJfdGhyZXNoID0gMC45LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcnRfd2luZG93ID0gMS82MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVfY29sID0gIm16X3J0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG16X2NvbCA9ICJteiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBydF9jb2wgPSAicnQiKQoKaGVhZChjb25uZWN0aW9uKQpgYGAKCiMjIENsdXN0ZXJpbmcKYGBge3J9CmNsdXN0ZXJzIDwtIGZpbmRfY2x1c3RlcnMoY29ubmVjdGlvbnMgPSBjb25uZWN0aW9uLCBkX3RocmVzaCA9IDAuOCkKYGBgCgpgYGB7cn0KIyBhc3NpZ24gYSBjbHVzdGVyIElEIHRvIGFsbCBmZWF0dXJlcy4gQ2x1c3RlcnMgYXJlIG5hbWVkIGFmdGVyIGZlYXR1cmUgd2l0aCBoaWdoZXN0IG1lZGlhbiBwZWFrIGhlaWdodApmZWF0dXJlc19jbHVzdGVyZWQgPC0gYXNzaWduX2NsdXN0ZXJfaWQoZGF0YV9ub3RhbWUsIGNsdXN0ZXJzLCBmZWF0dXJlcywgbmFtZV9jb2wgPSAibXpfcnQiKQoKCiMgdmlzdWFsaXplIGNsdXN0ZXJzCiN2aXN1YWxpemVfY2x1c3RlcnMoZGF0YV9ub3RhbWUsIGZlYXR1cmVzLCBjbHVzdGVycywgbWluX3NpemUgPSAzLCBydF93aW5kb3cgPSAyLG5hbWVfY29sID0gIm16X3J0IiwgbXpfY29sID0gIm16IiwgcnRfY29sID0gInJ0IiwgZmlsZV9wYXRoID0gIn4vcGF0aC90by9wcm9qZWN0LyIpCgojIGxldHMgc2VlIGhvdyBtYW55IGZlYXR1cmVzIGFyZSByZW1vdmVkIHdoZW4gd2Ugb25seSBrZWVwIG9uZSBmZWF0dXJlIHBlciBjbHVzdGVyCnB1bGxlZCA8LSBwdWxsX2NsdXN0ZXJzKGRhdGFfbm90YW1lLCBmZWF0dXJlc19jbHVzdGVyZWQsIG5hbWVfY29sID0gIm16X3J0IikKY2x1c3Rlcl9kYXRhIDwtIHB1bGxlZCRjZGF0YQoKY2x1c3Rlcl9mZWF0dXJlcyA8LSBwdWxsZWQkY2ZlYXR1cmVzCiMgZXhwb3J0IGNsdXN0ZXJlZCBmZWF0dXJlIGxpc3QKd3JpdGVfY3N2KGNsdXN0ZXJfZmVhdHVyZXMsCiAgICAgICAgICAiY2x1c3Rlcl9mZWF0dXJlcy1faGlsaWMtcG9zLmNzdiIpCgpucm93KG9taWNzZGF0YSkgLSBucm93KGNsdXN0ZXJfZmVhdHVyZXMpCmBgYAoKIyMgUmVkdWNlIGRhdGFzZXQgYmFzZWQgb24gY2x1c3RlcmluZwpgYGB7cn0KIyB0cmFuc3Bvc2UgdGhlIGZ1bGwgZGF0YXNldCBiYWNrIHRvIHdpZGUgc28gdGhhdCBpdCBpcyBtb3JlIHNpbWlsYXIgdG8gdGhlIG5vdGFtZSBkYXRhc2V0CmltcHV0ZWRfZnVsbGRhdGFfd2lkZSA8LSBpbXB1dGVkX2Z1bGxkYXRhICU+JQogIGRwbHlyOjpzZWxlY3QoLSJyb3dfSUQiKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbXpfcnQsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBwZWFrX2hlaWdodCkKCiMgbGlzdCBvZiByZWR1Y2VkIGZlYXR1cmVzCmNsdXN0ZXJuYW1lcyA8LSBjbHVzdGVyX2ZlYXR1cmVzJG16X3J0CgojIGRwbHlyOjpzZWxlY3Qgb25seSB0aGUgZmVhdHVyZXMgYXJlIGluIHRoZSByZWR1Y2VkIGxpc3QKaW1wX2NsdXN0IDwtIGltcHV0ZWRfZnVsbGRhdGFfd2lkZVssYyhuYW1lcyhpbXB1dGVkX2Z1bGxkYXRhX3dpZGUpICVpbiUgY2x1c3Rlcm5hbWVzKV0KCiMgYmluZCBiYWNrIHNhbXBsZSBuYW1lcwppbXBfY2x1c3QgPC0gY2JpbmQoaW1wdXRlZF9mdWxsZGF0YV93aWRlWzFdLCBpbXBfY2x1c3QpCgp0aWJibGUoaW1wX2NsdXN0KQoKYGBgCgojIyBNeiB2cyBSVCBzY2F0dGVycGxvdCAKCmBgYHtyfQojIHBsb3QgbmV3IHJ0IHZzIG16IHNjYXR0ZXJwbG90IHBvc3QtY2x1c3RlcmluZwoocGxvdF9tenZzcnRfcG9zdGNsdXN0ZXIgPC0gY2x1c3Rlcl9mZWF0dXJlcyAlPiUKICBnZ3Bsb3QoYWVzKHggPSBydCwKICAgICAgICAgICAgIHkgPSBteikpICsKICBnZW9tX3BvaW50KCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh4ID0gIlJldGVudGlvbiB0aW1lLCBtaW4iLAogICAgICAgeSA9ICJtL3osIG5ldXRyYWwiLAogICAgICAgdGl0bGUgPSAibXogYWNyb3NzIFJUIGZvciBhbGwgZmVhdHVyZXMgYWZ0ZXIgY2x1c3RlcmluZyIpKQoKCmBgYAoKCmBgYHtyfQojIHBsb3QgYm90aCBzY2F0dGVycGxvdHMgdG8gY29tcGFyZSB3aXRoIGFuZCB3aXRob3V0IG5vdGFtZSBjbHVzdGVyaW5nCihzY2F0dGVycGxvdHMgPC0gZ2dhcnJhbmdlKHBsb3RfbXp2c3J0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF9tenZzcnRfcG9zdGNsdXN0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gMikpCmBgYAoKIyBCaW5kIG1ldGEgZGF0YQpgYGB7cn0KaW1wX21ldGFiaW5kX2NsdXN0IDwtIHJpZ2h0X2pvaW4obWV0YWRhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbXBfY2x1c3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInNhbXBsZV9JRCIpCmBgYAoKIyBWaXN1YWxpemUgdW50cmFuc2Zvcm1lZCBkYXRhCgojIyBEYXRhIHdyYW5nbGluZwpgYGB7cn0KIyBjaGFuZ2UgbWV0YSBkYXRhIGNvbHVtbnMgdG8gY2hhcmFjdGVyIHNvIHRoYXQgSSBjYW4gY2hhbmdlIE5BcyBmcm9tIFFDcyB0byAiUUMiCmltcF9tZXRhYmluZF9jbHVzdCA8LSBpbXBfbWV0YWJpbmRfY2x1c3QgJT4lCiAgbXV0YXRlX2F0KGMoIlN1YmplY3QiLAogICAgICAgICAgICAgICJQZXJpb2QiLAogICAgICAgICAgICAgICJJbnRlcnZlbnRpb24iLAogICAgICAgICAgICAgICJwcmVfcG9zdCIsCiAgICAgICAgICAgICAgInNlcXVlbmNlIiwKICAgICAgICAgICAgICAiSW50ZXJ2ZW50aW9uX3dlZWsiLAogICAgICAgICAgICAgICJTZXgiLAogICAgICAgICAgICAgICJBZ2UiLAogICAgICAgICAgICAgICJCTUkiKSwKICAgICAgICAgICAgYXMuY2hhcmFjdGVyKSAKCiMgcmVwbGFjZSBOQXMgaW4gbWV0YWRhdGEgY29sdW1ucyBmb3IgUUNzCmltcF9tZXRhYmluZF9jbHVzdFtpcy5uYShpbXBfbWV0YWJpbmRfY2x1c3QpXSA8LSAiUUMiCgojIHVuaXRlIHByZV9wb3N0IGNvbHVtbiB3aXRoIGludGVydmVudGlvbiBjb2x1bW4gdG8gY3JlYXRlIHByZV9pbnRlcnZlbnRpb24gY29sdW1uCmltcF9tZXRhYmluZF9jbHVzdCA8LSBpbXBfbWV0YWJpbmRfY2x1c3QgJT4lCiAgdW5pdGUoY29sID0gInByZV9wb3N0X2ludGVydmVudGlvbiIsCiAgICAgICAgYygicHJlX3Bvc3QiLCJJbnRlcnZlbnRpb24iKSwKICAgICAgICBzZXAgPSAiXyIsCiAgICAgICAgcmVtb3ZlID0gRkFMU0UpCgojIGxvbmcgZGYKaW1wX21ldGFiaW5kX2NsdXN0X3RpZHkgPC0gaW1wX21ldGFiaW5kX2NsdXN0ICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gMTI6bmNvbCguKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibXpfcnQiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAicmVsX2FidW5kIikKCiMgc3RydWN0dXJlIGNoZWNrCnN0cihpbXBfbWV0YWJpbmRfY2x1c3RfdGlkeSkKYGBgCgoKIyMgQm94cGxvdApgYGB7cn0KaW1wX21ldGFiaW5kX2NsdXN0X3RpZHkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc2FtcGxlX0lELCB5ID0gcmVsX2FidW5kLCBjb2xvciA9IEludGVydmVudGlvbikpICsKICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjYpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygibGlnaHQgZ3JleSIsICJ0b21hdG8xIiwgImdvbGQiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsKICBsYWJzKHRpdGxlID0gIkxDLU1TICgrKSBGZWF0dXJlIEFidW5kYW5jZXMgYnkgU2FtcGxlIiwKICAgICAgIHN1YnRpdGxlID0gIlVuc2NhbGVkIGRhdGEiLAogICAgICAgeSA9ICJSZWxhdGl2ZSBhYnVuZGFuY2UiKQpgYGAKV2lsbCBuZWVkIHRvIGxvZyB0cmFuc2Zvcm0gaW4gb3JkZXIgdG8gbm9ybWFsaXplIGFuZCBhY3R1YWxseSBzZWUgdGhlIGRhdGEKCiMgTG9nMiB0cmFuc2Zvcm0KYGBge3J9CmltcF9tZXRhYmluZF9jbHVzdF90aWR5X2xvZzIgPC0gaW1wX21ldGFiaW5kX2NsdXN0X3RpZHkgJT4lCiAgbXV0YXRlKHJlbF9hYnVuZF9sb2cyID0gaWZfZWxzZShyZWxfYWJ1bmQgPiAwLCBsb2cyKHJlbF9hYnVuZCksIDApKSAlPiUKICByZXBsYWNlKGlzLm5hKC4pLCAwKQpgYGAKCiMjIEJveHBsb3QKYGBge3J9CihicF9kYXRhX3F1YWxpdHkgPC0gaW1wX21ldGFiaW5kX2NsdXN0X3RpZHlfbG9nMiAlPiUKICBnZ3Bsb3QoYWVzKHggPSBzYW1wbGVfSUQsIHkgPSByZWxfYWJ1bmRfbG9nMiwgZmlsbCA9IEludGVydmVudGlvbikpICsKICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjYpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJsaWdodCBncmV5IiwgInRvbWF0bzEiLCAiZ29sZCIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKwogIGxhYnModGl0bGUgPSAiTEMtTVMgKCspIEZlYXR1cmUgQWJ1bmRhbmNlcyBieSBTYW1wbGUiLAogICAgICAgc3VidGl0bGUgPSAiTG9nMiB0cmFuc2Zvcm1lZCBkYXRhIiwKICAgICAgIHkgPSAiUmVsYXRpdmUgYWJ1bmRhbmNlIikpCmBgYAoKTXVjaCBiZXR0ZXIhIFFDcyBsb29rIGxpa2UgdGhlcmUgbWF5IGJlIHNvbWUgZHJpZnQgdGhvdWdoLiBMZXQncyBkbyBkcmlmdCBjb3JyZWN0aW9uIHdpdGggdGhlIE5vdGFtZSBwYWNrYWdlIQoKIyBOb3RhbWUgZHJpZnQgY29ycmVjdGlvbgoKIyMgRGF0YSB3cmFuZ2xpbmcKCiMjIyBGZWF0dXJlIGFidW5kIGRmIAoKYGBge3J9CiMgZmlsdGVyZWQgYW5kIGltcHV0ZWQgZGF0YSBhZnRlciBub3RhbWUgY2x1c3RlcmluZywgdHJhbnNwb3NlZApmZWF0dXJlc190ZXN0Zm9yUUNjb3JyIDwtIHQoaW1wX2NsdXN0KSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcm93X3RvX25hbWVzKHJvd19udW1iZXIgPSAiZmluZF9oZWFkZXIiKQoKIyBsb2cyIHRyYW5zZm9ybQpsb2cyX2ZlYXR1cmVzX3Rlc3Rmb3JRQ2NvcnIgPC0gZmVhdHVyZXNfdGVzdGZvclFDY29yciAlPiUKICBtdXRhdGVfYWxsKGFzLm51bWVyaWMpICU+JQogIGxvZzIoKQoKCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQojIHdyaXRlIGNzdiB0byBtYW51YWxseSBlZGl0CndyaXRlLmNzdihsb2cyX2ZlYXR1cmVzX3Rlc3Rmb3JRQ2NvcnIsCiAgICAgICAgICAibm90YW1lIGRmcy9mZWF1cmVzX3Rlc3QuY3N2IiwKICAgICAgICAgIHJvdy5uYW1lcyA9IFRSVUUpCmBgYAoKCkltcG9ydCBjb3JyZWN0ZWQgZGYgKGVkaXRlZCBzbyB0aGF0IG16X3J0IGNvdWxkIHJvd25hbWUgMSkKYGBge3J9CmZlYXR1cmVzX2ZvclFDY29yciA8LSByZWFkLmNzdigibm90YW1lIGRmcy9mZWF0dXJlc19mb3JRQ2NvcnIuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxKQoKCmZlYXR1cmVzX2ZvclFDY29yciA8LSBmZWF0dXJlc19mb3JRQ2NvcnIgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJtel9ydCIpICU+JQogIHJvd190b19uYW1lcyhyb3dfbnVtYmVyID0gMSklPiUKICBzZXBhcmF0ZShjb2wgPSBtel9ydCwKICAgICAgICAgICBpbnRvID0gYygibXoiLCAicnQiKSwKICAgICAgICAgICBzZXAgPSAiXyIpCgoKCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQp3cml0ZS5jc3YoZmVhdHVyZXNfZm9yUUNjb3JyLAogICAgICAgICAgIm5vdGFtZSBkZnMvZmVhdHVyZXNfZm9yUUNjb3JyX3B0Mi5jc3YiLAogICAgICAgICAgcm93Lm5hbWVzID0gVFJVRSkKYGBgCgoKIyMjIFBoZW5vIGRmCgpgYGB7cn0KIyBzZXBhcmF0ZSBzYW1wbGVJRCBhbmQgaW5qZWN0aW9uIG9yZGVyCnBoZW5vX2RhdGEgPC0gaW1wX2NsdXN0WzFdICU+JQogIHNlcGFyYXRlKGNvbCA9IHNhbXBsZV9JRCwKICAgICAgICAgICBpbnRvID0gYygic2FtcGxlX0lEIiwgImluamVjdGlvbl9vcmRlciIpLAogICAgICAgICAgIHNlcCA9ICJfSElMSUNQT1NfIikKCiMgY2hhbmdlIHByZS1ydW4gUUMgaW5qZWN0aW9uIG51bWJlciB0byBjb3JyZWN0IG51bWJlcgpwaGVub19kYXRhWzQ4LCAiaW5qZWN0aW9uX29yZGVyIl0gPC0gOQoKIyBtYWtlIGluaiBvcmRlciBjb2x1bW4gbnVtCnBoZW5vX2RhdGEgPC0gcGhlbm9fZGF0YSAlPiUKICBtdXRhdGVfYXQoImluamVjdGlvbl9vcmRlciIsIGFzLm51bWVyaWMpCgp0X3BoZW5vX2RhdGEgPC0gYXMuZGF0YS5mcmFtZSh0KHBoZW5vX2RhdGEpKQoKCmBgYAoKQ29tYmluZSBwaGVubyBhbmQgZmVhdHVyZSBkZnMgbWFudWFsbHkgaW4gZXhjZWwgdG8gY3JlYXRlIG1ldGFib3NldCBkZi4KYGBge3IsIGV2YWw9RkFMU0V9CndyaXRlLmNzdih0X3BoZW5vX2RhdGEsCiAgICAgICAgICAibm90YW1lIGRmcy9waGVub19kZi5jc3YiLAogICAgICAgICAgcm93Lm5hbWVzID0gVFJVRSkKYGBgCgoKIyMgSW1wb3J0IE1ldGFib3NldAoKYGBge3J9CiNtYWtlIHN1cmUgd2hlbiBjb252ZXJ0aW5nIGNzdiB0byB4bHN4IHRoYXQgeW91IHNhdmUgYXMgYSBuZXcgZmlsZSwgZG9uJ3QganVzdCBjaGFuZ2UgdGhlIG5hbWUgb2YgdGhlIGZpbGUKbWV0YWJvc2V0IDwtIHJlYWRfZnJvbV9leGNlbCgibm90YW1lIGRmcy9tZXRhYm9zZXQueGxzeCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXRfYnkgPSBjKCJjb2x1bW4iLCAiSW9uIG1vZGUiKSkKCmBgYAoKCmBgYHtyfQojY29uc3RydWN0IE1ldGFib3NldAptb2RlcyA8LSBjb25zdHJ1Y3RfbWV0YWJvc2V0cyhleHBycyA9IG1ldGFib3NldCRleHBycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9fZGF0YSA9IG1ldGFib3NldCRwaGVub19kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlX2RhdGEgPSBtZXRhYm9zZXQkZmVhdHVyZV9kYXRhLCBncm91cF9jb2wgPSAiQ2xhc3MiKQoKI2V4dHJhY3QgZWFjaCBtb2RlIGludG8gYSBzaW5nbGUgb2JqZWN0Cm1vZGUgPC0gbW9kZXMkSElMSUNfcG9zCmBgYAoKCiMjIEJveHBsb3RzIGJlZm9yZSBjb3JyZWN0aW9uCgpgYGB7cn0KCihxdWFsaXR5QlBzX2I0Y29ycmVjdGlvbiA8LSBwbG90X3NhbXBsZV9ib3hwbG90cyhtb2RlLCBvcmRlcl9ieSA9ICJDbGFzcyIsIHRpdGxlID0gIlVuY29ycmVjdGVkIGZlYXR1cmUgYWJ1bmRhbmNlIikpCgpgYGAKCgojIyBCb3hwbG90cyBhZnRlciBRQyBkcmlmdCBjb3JyZWN0aW9uCgpkcmlmdCBjb3JyZWN0ZWQgdGFrZXMgdXAgdG8gMiBtaW51dGVzCmBgYHtyfQptb2RlIDwtIGZsYWdfZGV0ZWN0aW9uKG1vZGUsIHFjX2xpbWl0ID0gMC43NSwgZ3JvdXBfbGltaXQgPSAwLjgpCgoKY29ycmVjdGVkIDwtIGNvcnJlY3RfZHJpZnQobW9kZSwgbG9nX3RyYW5zZm9ybSA9IEZBTFNFKQpgYGAKCgojIyMgRGlkIGRyaWZ0IGNvcnJlY3Rpb24gd29yaz8KCmluc3BlY3Rpb24gYWxzbyB0YWtlcyBhYm91dCAyIG1pbnV0ZXMgdG8gcnVuOyBvdXRwdXQgaXMgcGVyY2VudCBvZiB0aGUgZmVhdHVyZXMgdGhhdCB3ZXJlIGRyaWZ0IGNvcnJlY3RlZC4gVGhlIHJlbWFpbmluZyAibG93LXF1YWxpdHkiIHBlcmNlbnQgcmVwcmVzZW50cyBmZWF0dXJlcyBmb3Igd2hpY2ggdGhlIERDIGRpZCAqbm90KiBpbXByb3ZlIHRoZSBSU0QgYW5kIEQtcmF0aW8gb2YgdGhlIG9yaWdpbmFsIGRhdGEuCgpgYGB7cn0KaW5zcGVjdGVkIDwtIGluc3BlY3RfZGMob3JpZyA9IG1vZGUsIGRjID0gY29ycmVjdGVkLCBjaGVja19xdWFsaXR5ID0gVFJVRSkKYGBgCgojIyMgQm94cGxvdHMsIGNvcnJlY3RlZApgYGB7cn0KCihxdWFsaXR5QlBTX2RyaWZ0Y29ycmVjdGlvbiA8LSBwbG90X3NhbXBsZV9ib3hwbG90cyhjb3JyZWN0ZWQsIG9yZGVyX2J5ID0gIkNsYXNzIiwgdGl0bGUgPSAiQ29ycmVjdGVkIGZlYXR1cmUgYWJ1bmRhbmNlIikpCgoKYGBgCgoKIyMgQ29tcGFyZSBxdWFsaXR5IEJQcwpgYGB7ciwgZmlnLmhlaWdodD0xMH0KKHF1YWxpdHlCUHNfY29tcGFyZWQgPC0gZ2dhcnJhbmdlKHF1YWxpdHlCUHNfYjRjb3JyZWN0aW9uLCBxdWFsaXR5QlBTX2RyaWZ0Y29ycmVjdGlvbiwKICAgICAgICAgICAgICAgICAgICBuY29sID0gMSwgbnJvdyA9IDIpKQpgYGAKCgojIyBFeHBvcnQgbmV3IE1ldGFib3NldCB0byBFeGNlbCBzcHJlYWRzaGVldApgYGB7ciBldmFsID0gRkFMU0V9CndyaXRlX3RvX2V4Y2VsKGNvcnJlY3RlZCwgIm5vdGFtZSBkZnMvbWV0YWJvc2V0X2NvcnJlY3RlZC54bHN4IikKYGBgCgoKIyMgSW1wb3J0IGVkaXRlZCBNZXRhYm9zZXQKCmBgYHtyfQptZXRhYmRhdGFfY29ycmVjdGVkIDwtIHJlYWQuY3N2KGZpbGUgPSAibm90YW1lIGRmcy9tZXRhYm9zZXRfY29ycmVjdGVkX2VkaXRlZGZvclIuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEZBTFNFKQpgYGAKCgojIyBXcmFuZ2xlIG5ldyBtZXRhYiBkYXRhCgojIyMgQ29tYmluZSBteiAmIHJ0IGJhY2sgdG9nZXRoZXIKCmBgYHtyfQptZXRhYmRhdGFfY29ycmVjdGVkX01aX1JUIDwtIG1ldGFiZGF0YV9jb3JyZWN0ZWQgJT4lCiAgbXV0YXRlKG1hc3MgPSByb3VuZChtZXRhYmRhdGFfY29ycmVjdGVkJG1hc3MsIGRpZ2l0cyA9IDQpLCAjIERlY3JlYXNlIG51bWJlciBvZiBkZWNpbWFscyBmb3IgbS96ICYgcnQKICAgICAgICAgcnQgPSByb3VuZChtZXRhYmRhdGFfY29ycmVjdGVkJHJ0LCBkaWdpdHMgPSAzKSwKICAgICAgICAgLmJlZm9yZT0xLAogICAgICAgICAua2VlcD0idW51c2VkIikgJT4lCiAgdW5pdGUobXpfcnQsIGMobWFzcywgcnQpLCByZW1vdmU9VFJVRSkgIyBDb21iaW5lIG0veiAmIHJ0IHdpdGggXyBpbiBiZXR3ZWVuCgpgYGAKCiMjIyBUcmFuc3Bvc2UgbmV3IGRmCgpgYGB7cn0KbWV0YWJkYXRhX2NvcnJlY3RlZF90IDwtIGFzLmRhdGEuZnJhbWUodChtZXRhYmRhdGFfY29ycmVjdGVkX01aX1JUKSkgJT4lCiAgcm93X3RvX25hbWVzKHJvd19udW1iZXIgPSAiZmluZF9oZWFkZXIiKSAlPiUgIyBtYWtlIE1aX1JUIGNvbHVtbiBuYW1lcwogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAic3Vial9wZXJpb2QiKSAjIGNoYW5nZSByb3duYW1lcyB0byBjb2x1bW4gMQogIApgYGAKCiMjIyBCaW5kIG5ldyBkYXRhIHdpdGggbWV0YWRhdGEKCkkgd2FudCB0aGUgbmV3IGRyaWZ0IGNvcnJlY3RlZCAoREMpIGRmIHRvIGxvb2sganVzdCBsaWtlICJpbXBfbWV0YWJpbmRfY2x1c3RfbG9nMiIgZGYKCmBgYHtyfQojIGdvIGJhY2sgdG8gd2lkZSBkYXRhCmltcF9tZXRhYmluZF9jbHVzdF9sb2cyIDwtIGltcF9tZXRhYmluZF9jbHVzdF90aWR5X2xvZzIgJT4lCiAgZHBseXI6OnNlbGVjdCghcmVsX2FidW5kKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbXpfcnQsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSByZWxfYWJ1bmRfbG9nMikKYGBgCgoKYGBge3J9CiMgY29tYmluZSBzdWJqZWN0IGFuZCBwZXJpb2QgY29sdW1ucyBmcm9tIGltcF9tZXRhYmluZF9jbHVzdF9sb2cyIGluIG9yZGVyIHRvIG1pbWljIERDIGRmCnN1YmpfcGVyX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyIDwtIGltcF9tZXRhYmluZF9jbHVzdF9sb2cyICU+JQogIHVuaXRlKHN1YmpfcGVyaW9kLCBjKFN1YmplY3QsIFBlcmlvZCksIHJlbW92ZSA9IEZBTFNFKQoKIyBwbGFjZSBuZXcgREMgb2JzZXJ2YXRpb25zIGluCkRDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyIDwtIGZ1bGxfam9pbihzdWJqX3Blcl9pbXBfbWV0YWJpbmRfY2x1c3RfbG9nMlssYygxOjEyKV0sIG1ldGFiZGF0YV9jb3JyZWN0ZWRfdCwgYnkgPSAic3Vial9wZXJpb2QiKQoKIyB0YWtlIG91dCBvbGQgUUMgb2JzZXJ2YXRpb25zCkRDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyIDwtIERDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyICU+JQogIGZpbHRlcihzdWJqX3BlcmlvZCAhPSAiUUNfUUMiKQoKIyByZXBsYWNlIE5BcyBpbiBjb2x1bW5zIGZvciBRQ3MKRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzJbaXMubmEoRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzIpXSA8LSAiUUMiCmBgYAoKCiMgUENBcwoKIyMgV2l0aCBRQ1MKCiMjIyBXcmFuZ2xlCgpEYXRhIGFmdGVyIGRyaWZ0IGNvcnJlY3Rpb24KYGBge3J9CkRDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyIDwtIERDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyICU+JQogIGRwbHlyOjpzZWxlY3QoISJzdWJqX3BlcmlvZCIpICU+JQogIG11dGF0ZV9hdCgtYygxOjExKSwgYXMubnVtZXJpYykKYGBgCgoKYGBge3J9ClBDQS5EQ19pbXBfbWV0YWJpbmRfY2x1c3RfbG9nMiA8LSBQQ0EoRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzIsICAjIHdpZGUgZGF0YQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YWxpLnN1cCA9IDE6MTEsICMgcmVtb3ZlIHF1YWxpdGF0aXZlIHZhcmlhYmxlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UsICMgZG9uJ3QgZ3JhcGgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZS51bml0ID0gRkFMU0UpICMgZG9uJ3Qgc2NhbGUsIGFscmVhZHkgdHJhbnNmb3JtZWQgZGF0YQoKIyBQQ0Egc3VtbWFyeQprYWJsZShzdW1tYXJ5KFBDQS5EQ19pbXBfbWV0YWJpbmRfY2x1c3RfbG9nMikpCmBgYAoKYGBge3J9CiMgcHVsbCBQQyBjb29yZGluYXRlcyBpbnRvIGRmClBDX2Nvb3JkX1FDX2xvZzIgPC0gYXMuZGF0YS5mcmFtZShQQ0EuRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzIkaW5kJGNvb3JkKQoKIyBiaW5kIGJhY2sgbWV0YWRhdGEgZnJvbSBjb2xzIDEtMTAKUENfY29vcmRfUUNfbG9nMiA8LSBiaW5kX2NvbHMoRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzJbLDE6MTFdLCBQQ19jb29yZF9RQ19sb2cyKQoKIyBncmFiIHNvbWUgdmFyaWFuY2UgZXhwbGFpbmVkCmltcG9ydGFuY2VfUUMgPC0gUENBLkRDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyJGVpZwoKIyBzZXQgdmFyaWFuY2UgZXhwbGFpbmVkIHdpdGggUEMxLCByb3VuZCB0byAyIGRpZ2l0cwpQQzFfd2l0aFFDIDwtIHJvdW5kKGltcG9ydGFuY2VfUUNbMSwyXSwgMikKCiMgc2V0IHZhcmlhbmNlIGV4cGxhaW5lZCB3aXRoIFBDMiwgcm91bmQgdG8gMiBkaWdpdHMKUEMyX3dpdGhRQyA8LSByb3VuZChpbXBvcnRhbmNlX1FDWzIsMl0sIDIpCmBgYAoKIyMjIFBsb3RzClVzaW5nIEZhY3RvRXh0cmEgcGFja2FnZQpgYGB7cn0KIyBzY3JlZSBwbG90CmZ2aXpfZWlnKFBDQS5EQ19pbXBfbWV0YWJpbmRfY2x1c3RfbG9nMikKCiMgZ2V0IGVpZ2VudmFsdWVzCmthYmxlKGdldF9laWcoUENBLkRDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyKSkKYGBgCgpgYGB7cn0KIyBzY29yZXMgcGxvdApmdml6X3BjYV9pbmQoUENBLkRDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyKQpgYGAKCiMjIyBNYW51YWwgc2NvcmVzIHBsb3RzCmBgYHtyfQojIG1hbnVhbCBzY29yZXMgcGxvdAooUENBX3dpdGhRQ3MgPC0gUENfY29vcmRfUUNfbG9nMiAlPiUKICBnZ3Bsb3QoYWVzKHggPSBEaW0uMSwgeSA9IERpbS4yLAogICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihJbnRlcnZlbnRpb24sIGxldmVscyA9IGMoIlllbGxvdyIsICJSZWQiLCAiUUMiKSkpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBhbHBoYSA9IDAuOCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdvbGQiLCAidG9tYXRvMSIsICJsaWdodCBncmV5IikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gImJsYWNrIikgKyAgCiAgdGhlbWVfbWluaW1hbCgpICsKICBjb29yZF9maXhlZChQQzJfd2l0aFFDL1BDMV93aXRoUUMpICsKICBsYWJzKHggPSBnbHVlOjpnbHVlKCJQQzE6IHtQQzFfd2l0aFFDfSUiKSwKICAgICAgIHkgPSBnbHVlOjpnbHVlKCJQQzI6IHtQQzJfd2l0aFFDfSUiKSwKICAgICAgIGZpbGwgPSAiR3JvdXAiLAogICAgICAgdGl0bGUgPSAiUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMgU2NvcmVzIFBsb3QiLAogICAgICAgc3VidGl0bGUgPSAiTG9nMiB0cmFuc2Zvcm1lZCBkYXRhIikpCmBgYAoKIyMgV2l0aG91dCBRQ3MKCiMjIyBXcmFuZ2xlIAoKYGBge3J9CkRDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyX25vUUNzIDwtIERDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyICU+JQogIGZpbHRlcihJbnRlcnZlbnRpb24gIT0gIlFDIikKClBDQS5EQ19pbXBfbWV0YWJpbmRfY2x1c3RfbG9nMl9ub1FDcyA8LSBQQ0EoRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzJfbm9RQ3MsICMgd2lkZSBkYXRhCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFsaS5zdXA9MToxMSwgIyByZW1vdmUgcXVhbGl0YXRpdmUgdmFyaWFibGVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmFwaD1GQUxTRSwgIyBkb24ndCBncmFwaAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUudW5pdD1GQUxTRSkgIyBkb24ndCBzY2FsZSwgd2UgYWxyZWFkeSBkaWQgdGhpcwoKIyBsb29rIGF0IHN1bW1hcnkKa2FibGUoc3VtbWFyeShQQ0EuRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzJfbm9RQ3MpKQpgYGAKCmBgYHtyfQojIHB1bGwgUEMgY29vcmRpbmF0ZXMgaW50byBkZgpQQ19jb29yZF9ub1FDc19sb2cyIDwtIGFzLmRhdGEuZnJhbWUoUENBLkRDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyX25vUUNzJGluZCRjb29yZCkKCiMgYmluZCBiYWNrIG1ldGFkYXRhIGZyb20gY29scyAxLTEwClBDX2Nvb3JkX25vUUNzX2xvZzIgPC0gYmluZF9jb2xzKERDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyX25vUUNzWywxOjExXSwgUENfY29vcmRfbm9RQ3NfbG9nMikKCiMgZ3JhYiBzb21lIHZhcmlhbmNlIGV4cGxhaW5lZAppbXBvcnRhbmNlX25vUUMgPC0gUENBLkRDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyX25vUUNzJGVpZwoKIyBzZXQgdmFyaWFuY2UgZXhwbGFpbmVkIHdpdGggUEMxLCByb3VuZCB0byAyIGRpZ2l0cwpQQzFfbm9RQyA8LSByb3VuZChpbXBvcnRhbmNlX25vUUNbMSwyXSwgMikKCiMgc2V0IHZhcmlhbmNlIGV4cGxhaW5lZCB3aXRoIFBDMiwgcm91bmQgdG8gMiBkaWdpdHMKUEMyX25vUUMgPC0gcm91bmQoaW1wb3J0YW5jZV9ub1FDWzIsMl0sIDIpCmBgYAoKIyMjIFBsb3RzClVzaW5nIEZhY3RvRXh0cmEKCmBgYHtyfQojIHNjcmVlIHBsb3QKZnZpel9laWcoUENBLkRDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyX25vUUNzKQpgYGAKCmBgYHtyfQojIHNjb3JlcyBwbG90CmZ2aXpfcGNhX2luZChQQ0EuRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzJfbm9RQ3MpCmBgYAoKCmBgYHtyfQojIHBsb3Qgb2YgY29udHJpYnV0aW9ucyBmcm9tIGZlYXR1cmVzIHRvIFBDMQoodmFyX2NvbnRyaWJfbm9RQ3NfUEMxIDwtIGZ2aXpfY29udHJpYihQQ0EuRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzJfbm9RQ3MsCiAgICAgICAgICAgICBjaG9pY2UgPSAidmFyIiwKICAgICAgICAgICAgIGF4ZXMgPSAxLAogICAgICAgICAgICAgdG9wID0gMjUsCiAgICAgICAgICAgICB0aXRsZSA9ICJWYXIgY29udHJpYnV0aW9uIHRvIFBDMTogbm8gUUNzIikpCgojIHBsb3Qgb2YgY29udHJpYnV0aW9ucyBmcm9tIGZlYXR1cmVzIHRvIFBDMgoodmFyX2NvbnRyaWJfbm9RQ3NfUEMyIDwtIGZ2aXpfY29udHJpYihQQ0EuRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzJfbm9RQ3MsCiAgICAgICAgICAgICBjaG9pY2UgPSAidmFyIiwKICAgICAgICAgICAgIGF4ZXMgPSAyLAogICAgICAgICAgICAgdG9wID0gMjUsCiAgICAgICAgICAgICB0aXRsZSA9ICJWYXIgY29udHJpYnV0aW9uIHRvIFBDMjogbm8gUUNzIikpCmBgYAoKIyMjIE1hbnVhbCBzY29yZXMgcGxvdHMKCiMjIyMgWWVsbG93IHZzIHJlZApgYGB7cn0KKFBDQV93aXRob3V0UUNzIDwtIFBDX2Nvb3JkX25vUUNzX2xvZzIgJT4lCiAgZ2dwbG90KGFlcyh4ID0gRGltLjEsIAogICAgICAgICAgICAgeSA9IERpbS4yLAogICAgICAgICAgICAgZmlsbCA9IEludGVydmVudGlvbikpICsKICAgIGdlb21fcG9pbnQoc2hhcGUgPSAyMSwgYWxwaGEgPSAwLjgpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGFscGhhPTAuNSkgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgYWxwaGE9MC41KSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJnb2xkIiwgInRvbWF0bzEiKSkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9ICJibGFjayIpICsgIAogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGNvb3JkX2ZpeGVkKFBDMl9ub1FDL1BDMV9ub1FDKSArCiAgICBsYWJzKHggPSBnbHVlOjpnbHVlKCJQQzE6IHtQQzFfbm9RQ30lIiksCiAgICAgICAgIHkgPSBnbHVlOjpnbHVlKCJQQzI6IHtQQzJfbm9RQ30lIiksCiAgICAgICAgIGZpbGwgPSAiSW50ZXJ2ZW50aW9uIiwKICAgICAgICAgdGl0bGUgPSAiUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMgU2NvcmVzIFBsb3QiLAogICAgICAgICBzdWJ0aXRsZSA9ICJMb2cyIHRyYW5zZm9ybWVkIGRhdGEsIHdpdGhvdXQgUUNzIikpCmdncGxvdGx5KFBDQV93aXRob3V0UUNzKQpgYGAKCiMjIyMgcHJlIHZzIHBvc3QKYGBge3J9CihQQ0Ffd2l0aG91dFFDcy5wcmVfcG9zdCA8LSBQQ19jb29yZF9ub1FDc19sb2cyICU+JQogIGdncGxvdChhZXMoeCA9IERpbS4xLCAKICAgICAgICAgICAgIHkgPSBEaW0uMiwKICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IocHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLCBsZXZlbHMgPSBjKCJwcmVfWWVsbG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwb3N0X1llbGxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHJlX1JlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicG9zdF9SZWQiKSksCiAgICAgICAgICAgICB0ZXh0ID0gc2FtcGxlX0lEKSkgKwogICAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBhbHBoYSA9IDAuOCkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgYWxwaGE9MC41KSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBhbHBoYT0wLjUpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyYXkiLCAieWVsbG93MSIsICJwaW5rMSIsICJyZWQyIikpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSAiYmxhY2siKSArICAKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBjb29yZF9maXhlZChQQzJfbm9RQy9QQzFfbm9RQykgKwogICAgbGFicyh4ID0gZ2x1ZTo6Z2x1ZSgiUEMxOiB7UEMxX25vUUN9JSIpLAogICAgICAgICB5ID0gZ2x1ZTo6Z2x1ZSgiUEMyOiB7UEMyX25vUUN9JSIpLAogICAgICAgICBmaWxsID0gInByZV9wb3N0IiwKICAgICAgICAgdGl0bGUgPSAiUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMgU2NvcmVzIFBsb3QiLAogICAgICAgICBzdWJ0aXRsZSA9ICJMb2cyIHRyYW5zZm9ybWVkLCB3aXRob3V0IFFDcyIpKQpnZ3Bsb3RseShQQ0Ffd2l0aG91dFFDcy5wcmVfcG9zdCwKICAgICAgICAgdG9vbHRpcCA9ICJ0ZXh0IikgCmBgYAoKIyMgTm90ZXMgCkxvb2tzIGxpa2Ugc3ViamVjdCA2MTA2IGFuZCA2MTEyIGFyZSBvdXRsaWVycwoKCiMjIFJlbW92YWwgb2Ygb3V0bGllcgoKIyMjIFdpdGggUUNzCgojIyMjIFdyYW5nbGUKCgpgYGB7cn0KIyBnbyBiYWNrIHRvIHdpZGUgZGF0YQpEQ19pbXBfbm9vdXRsaWVyc19sb2cyIDwtIERDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyICU+JQogIGZpbHRlcihTdWJqZWN0ICE9IDYxMDYsCiAgICAgICAgIFN1YmplY3QgIT0gNjExMikKClBDQS5EQ19pbXBfbm9vdXRsaWVyc19sb2cyIDwtIFBDQShEQ19pbXBfbm9vdXRsaWVyc19sb2cyLCAgIyB3aWRlIGRhdGEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFsaS5zdXAgPSAxOjExLCAjIHJlbW92ZSBxdWFsaXRhdGl2ZSB2YXJpYWJsZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmFwaCA9IEZBTFNFLCAjIGRvbid0IGdyYXBoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUudW5pdCA9IEZBTFNFKSAjIGRvbid0IHNjYWxlLCBhbHJlYWR5IHRyYW5zZm9ybWVkIGRhdGEKCiMgUENBIHN1bW1hcnkKc3VtbWFyeShQQ0EuRENfaW1wX25vb3V0bGllcnNfbG9nMikKYGBgCgpgYGB7cn0KIyBwdWxsIFBDIGNvb3JkaW5hdGVzIGludG8gZGYKUENfbm9vdXRsaWVyc19RQ19sb2cyIDwtIGFzLmRhdGEuZnJhbWUoUENBLkRDX2ltcF9ub291dGxpZXJzX2xvZzIkaW5kJGNvb3JkKQoKIyBiaW5kIGJhY2sgbWV0YWRhdGEgZnJvbSBjb2xzIDEtMTEKUENfbm9vdXRsaWVyc19RQ19sb2cyIDwtIGJpbmRfY29scyhEQ19pbXBfbm9vdXRsaWVyc19sb2cyWywxOjExXSwgUENfbm9vdXRsaWVyc19RQ19sb2cyKQoKIyBncmFiIHNvbWUgdmFyaWFuY2UgZXhwbGFpbmVkCmltcG9ydGFuY2Vfbm9vdXRsaWVyc19RQyA8LSBQQ0EuRENfaW1wX25vb3V0bGllcnNfbG9nMiRlaWcKCiMgc2V0IHZhcmlhbmNlIGV4cGxhaW5lZCB3aXRoIFBDMSwgcm91bmQgdG8gMiBkaWdpdHMKUEMxX25vb3V0bGllcnNfd2l0aFFDIDwtIHJvdW5kKGltcG9ydGFuY2Vfbm9vdXRsaWVyc19RQ1sxLDJdLCAyKQoKIyBzZXQgdmFyaWFuY2UgZXhwbGFpbmVkIHdpdGggUEMyLCByb3VuZCB0byAyIGRpZ2l0cwpQQzJfbm9vdXRsaWVyc193aXRoUUMgPC0gcm91bmQoaW1wb3J0YW5jZV9ub291dGxpZXJzX1FDWzIsMl0sIDIpCmBgYAoKIyMjIyBQbG90cwpVc2luZyBGYWN0b0V4dHJhIHBhY2thZ2UKYGBge3J9CiMgc2NyZWUgcGxvdApmdml6X2VpZyhQQ0EuRENfaW1wX25vb3V0bGllcnNfbG9nMikKYGBgCgpgYGB7cn0KIyBzY29yZXMgcGxvdApmdml6X3BjYV9pbmQoUENBLkRDX2ltcF9ub291dGxpZXJzX2xvZzIpCmBgYAoKCiMjIyMgTWFudWFsIHNjb3JlcyBwbG90cwoKICMjIyMjIFJlZCB2cyB5ZWxsb3cKYGBge3J9CiMgbWFudWFsIHNjb3JlcyBwbG90CihQQ0Ffbm9vdXRsaWVyc193aXRoUUNzIDwtIFBDX25vb3V0bGllcnNfUUNfbG9nMiAlPiUKICBnZ3Bsb3QoYWVzKHggPSBEaW0uMSwgeSA9IERpbS4yLAogICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihJbnRlcnZlbnRpb24sIGxldmVscyA9IGMoIlllbGxvdyIsICJSZWQiLCAiUUMiKSkpKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBhbHBoYSA9IDAuOCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdvbGQiLCAidG9tYXRvMSIsICJsaWdodCBncmV5IikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gImJsYWNrIikgKyAgCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHggPSBnbHVlOjpnbHVlKCJQQzE6IHtQQzFfbm9vdXRsaWVyc193aXRoUUN9JSIpLAogICAgICAgeSA9IGdsdWU6OmdsdWUoIlBDMjoge1BDMl9ub291dGxpZXJzX3dpdGhRQ30lIiksCiAgICAgICBmaWxsID0gIkdyb3VwIiwKICAgICAgIHRpdGxlID0gIlByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzIFNjb3JlcyBQbG90IikpCmBgYAoKIyMjIyMgUHJlIHZzIHBvc3QKYGBge3J9CihQQ0Ffbm9vdXRsaWVyc19wcmVwb3N0X3dpdGhRQ3MgPC0gUENfbm9vdXRsaWVyc19RQ19sb2cyICU+JQogIGdncGxvdChhZXMoeCA9IERpbS4xLCAKICAgICAgICAgICAgIHkgPSBEaW0uMiwKICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IocHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLCBsZXZlbHMgPSBjKCJwcmVfWWVsbG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwb3N0X1llbGxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHJlX1JlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicG9zdF9SZWQiKSksCiAgICAgICAgICAgICB0ZXh0ID0gc2FtcGxlX0lEKSkgKwogICAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBhbHBoYSA9IDAuOCkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgYWxwaGE9MC41KSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBhbHBoYT0wLjUpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyYXkiLCAieWVsbG93MSIsICJwaW5rMSIsICJyZWQyIikpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSAiYmxhY2siKSArICAKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKHggPSBnbHVlOjpnbHVlKCJQQzE6IHtQQzFfbm9vdXRsaWVyc193aXRoUUN9JSIpLAogICAgICAgICB5ID0gZ2x1ZTo6Z2x1ZSgiUEMyOiB7UEMyX25vb3V0bGllcnNfd2l0aFFDfSUiKSwKICAgICAgICAgZmlsbCA9ICJwcmVfcG9zdCIsCiAgICAgICAgIHRpdGxlID0gIlByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzIFNjb3JlcyBQbG90IikpCmdncGxvdGx5KFBDQV9ub291dGxpZXJzX3ByZXBvc3Rfd2l0aFFDcywKICAgICAgICAgdG9vbHRpcCA9ICJ0ZXh0IikgCmBgYAoKCiMjIyBXaXRob3V0IFFDcwoKIyMjIyBXcmFuZ2xlCmBgYHtyfQppbXBfbm9vdXRsaWVyc19ub1FDc19sb2cyIDwtIERDX2ltcF9ub291dGxpZXJzX2xvZzIgJT4lCiAgZmlsdGVyKEludGVydmVudGlvbiAhPSAiUUMiKSAKClBDQS5pbXBfbm9vdXRsaWVyc19ub1FDc19sb2cyIDwtIFBDQShpbXBfbm9vdXRsaWVyc19ub1FDc19sb2cyLCAjIHdpZGUgZGF0YQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbGkuc3VwPTE6MTEsICMgcmVtb3ZlIHF1YWxpdGF0aXZlIHZhcmlhYmxlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JhcGg9RkFMU0UsICMgZG9uJ3QgZ3JhcGgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLnVuaXQ9RkFMU0UpICMgZG9uJ3Qgc2NhbGUsIHdlIGFscmVhZHkgZGlkIHRoaXMKCiMgbG9vayBhdCBzdW1tYXJ5CmthYmxlKHN1bW1hcnkoUENBLmltcF9ub291dGxpZXJzX25vUUNzX2xvZzIpKQpgYGAKCmBgYHtyfQojIHB1bGwgUEMgY29vcmRpbmF0ZXMgaW50byBkZgpQQ19jb29yZF9ub291dGxpZXJzX25vUUNfbG9nMiA8LSBhcy5kYXRhLmZyYW1lKFBDQS5pbXBfbm9vdXRsaWVyc19ub1FDc19sb2cyJGluZCRjb29yZCkKCiMgYmluZCBiYWNrIG1ldGFkYXRhIGZyb20gY29scyAxLTExClBDX2Nvb3JkX25vb3V0bGllcnNfbm9RQ19sb2cyIDwtIGJpbmRfY29scyhpbXBfbm9vdXRsaWVyc19ub1FDc19sb2cyWywxOjExXSwgUENfY29vcmRfbm9vdXRsaWVyc19ub1FDX2xvZzIpCgojIGdyYWIgc29tZSB2YXJpYW5jZSBleHBsYWluZWQKaW1wb3J0YW5jZV9ub291dGxpZXJzX25vUUMgPC0gUENBLmltcF9ub291dGxpZXJzX25vUUNzX2xvZzIkZWlnCgojIHNldCB2YXJpYW5jZSBleHBsYWluZWQgd2l0aCBQQzEsIHJvdW5kIHRvIDIgZGlnaXRzClBDMV9ub291dGxpZXJzX25vUUMgPC0gcm91bmQoaW1wb3J0YW5jZV9ub291dGxpZXJzX25vUUNbMSwyXSwgMikKCiMgc2V0IHZhcmlhbmNlIGV4cGxhaW5lZCB3aXRoIFBDMiwgcm91bmQgdG8gMiBkaWdpdHMKUEMyX25vb3V0bGllcnNfbm9RQyA8LSByb3VuZChpbXBvcnRhbmNlX25vb3V0bGllcnNfbm9RQ1syLDJdLCAyKQpgYGAKCgojIyMjIFBsb3RzClVzaW5nIEZhY3RvRXh0cmEKYGBge3J9CiMgc2NyZWUgcGxvdApmdml6X2VpZyhQQ0EuaW1wX25vb3V0bGllcnNfbm9RQ3NfbG9nMikKYGBgCgpgYGB7cn0KIyBzY29yZXMgcGxvdApmdml6X3BjYV9pbmQoUENBLmltcF9ub291dGxpZXJzX25vUUNzX2xvZzIpCmBgYAoKCmBgYHtyfQojIHBsb3Qgb2YgY29udHJpYnV0aW9ucyBmcm9tIGZlYXR1cmVzIHRvIFBDMQoodmFyX2NvbnRyaWJfbm9vdXRsaWVyc19ub1FDc19QQzEgPC0gZnZpel9jb250cmliKFBDQS5pbXBfbm9vdXRsaWVyc19ub1FDc19sb2cyLAogICAgICAgICAgICAgY2hvaWNlID0gInZhciIsCiAgICAgICAgICAgICBheGVzID0gMSwKICAgICAgICAgICAgIHRvcCA9IDIwLAogICAgICAgICAgICAgdGl0bGUgPSAiVmFyIGNvbnRyaWJ1dGlvbiB0byBQQzE6IG5vIG91dGxpZXJzLCBubyBRQ3MiKSkKCiMgcGxvdCBvZiBjb250cmlidXRpb25zIGZyb20gZmVhdHVyZXMgdG8gUEMyCih2YXJfY29udHJpYl9ub291dGxpZXJzX25vUUNzX1BDMiA8LSBmdml6X2NvbnRyaWIoUENBLmltcF9ub291dGxpZXJzX25vUUNzX2xvZzIsCiAgICAgICAgICAgICBjaG9pY2UgPSAidmFyIiwKICAgICAgICAgICAgIGF4ZXMgPSAyLAogICAgICAgICAgICAgdG9wID0gMjAsCiAgICAgICAgICAgICB0aXRsZSA9ICJWYXIgY29udHJpYnV0aW9uIHRvIFBDMjogbm8gb3V0bGllcnMsIG5vIFFDcyIpKQpgYGAKCgojIyMjIE1hbnVhbCBzY29yZXMgcGxvdHMKCiMjIyMjIFJlZCB2cyB5ZWxsb3cKYGBge3J9CihQQ0Ffbm9vdXRsaWVyc193aXRob3V0UUNzIDwtIFBDX2Nvb3JkX25vb3V0bGllcnNfbm9RQ19sb2cyICU+JQogIGdncGxvdChhZXMoeCA9IERpbS4xLCAKICAgICAgICAgICAgIHkgPSBEaW0uMiwKICAgICAgICAgICAgIGZpbGwgPSBJbnRlcnZlbnRpb24pKSArCiAgICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIGFscGhhID0gMC44KSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBhbHBoYT0wLjUpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGFscGhhPTAuNSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygidG9tYXRvMSIsICJnb2xkIikpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSAiYmxhY2siKSArICAKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBjb29yZF9maXhlZChQQzJfbm9vdXRsaWVyc19ub1FDL1BDMV9ub291dGxpZXJzX25vUUMpICsKICAgIGxhYnMoeCA9IGdsdWU6OmdsdWUoIlBDMToge1BDMV9ub291dGxpZXJzX25vUUN9JSIpLAogICAgICAgICB5ID0gZ2x1ZTo6Z2x1ZSgiUEMyOiB7UEMyX25vb3V0bGllcnNfbm9RQ30lIiksCiAgICAgICAgIGZpbGwgPSAiSW50ZXJ2ZW50aW9uIiwKICAgICAgICAgdGl0bGUgPSAiUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMgU2NvcmVzIFBsb3QiKSkKZ2dwbG90bHkoUENBX25vb3V0bGllcnNfd2l0aG91dFFDcykKYGBgCgoKIyMjIyMgUHJlIHZzIHBvc3QKYGBge3J9CihQQ0Ffbm9vdXRsaWVyc19ub1FDcy5wcmVwb3N0IDwtIFBDX2Nvb3JkX25vb3V0bGllcnNfbm9RQ19sb2cyICU+JQogIGdncGxvdChhZXMoeCA9IERpbS4xLCAKICAgICAgICAgICAgIHkgPSBEaW0uMiwKICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IocHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLCBsZXZlbHMgPSBjKCJwcmVfWWVsbG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwb3N0X1llbGxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHJlX1JlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicG9zdF9SZWQiKSksCiAgICAgICAgICAgICB0ZXh0ID0gc2FtcGxlX0lEKSkgKwogICAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBhbHBoYSA9IDAuOCkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgYWxwaGE9MC41KSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBhbHBoYT0wLjUpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyYXkiLCAieWVsbG93MSIsICJwaW5rMSIsICJyZWQyIikpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSAiYmxhY2siKSArICAKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBjb29yZF9maXhlZChQQzJfbm9vdXRsaWVyc19ub1FDL1BDMV9ub291dGxpZXJzX25vUUMpICsKICAgIGxhYnMoeCA9IGdsdWU6OmdsdWUoIlBDMToge1BDMV9ub291dGxpZXJzX25vUUN9JSIpLAogICAgICAgICB5ID0gZ2x1ZTo6Z2x1ZSgiUEMyOiB7UEMyX25vb3V0bGllcnNfbm9RQ30lIiksCiAgICAgICAgIGZpbGwgPSAicHJlX3Bvc3QiLAogICAgICAgICB0aXRsZSA9ICJQcmluY2lwYWwgQ29tcG9uZW50cyBBbmFseXNpcyBTY29yZXMgUGxvdCIsCiAgICAgICAgIHN1YnRpdGxlID0gIkxvZzIgdHJhbnNmb3JtZWQgZGF0YSwgbm8gb3V0bGllcnMiKSkKZ2dwbG90bHkoUENBX25vb3V0bGllcnNfbm9RQ3MucHJlcG9zdCwKICAgICAgICAgdG9vbHRpcCA9ICJ0ZXh0IikgCmBgYAoKCiMjIyMjIE0gdiBGCmBgYHtyfQooUENBX25vb3V0bGllcnNfbm9RQ3MuTXZzRiA8LSBQQ19jb29yZF9ub291dGxpZXJzX25vUUNfbG9nMiAlPiUKICBnZ3Bsb3QoYWVzKHggPSBEaW0uMSwgCiAgICAgICAgICAgICB5ID0gRGltLjIsCiAgICAgICAgICAgICBmaWxsID0gZmFjdG9yKFNleCwgbGV2ZWxzID0gYygiTSIsIkYiKSksCiAgICAgICAgICAgICB0ZXh0ID0gc2FtcGxlX0lEKSkgKwogICAgZ2VvbV9wb2ludChzaGFwZSA9IDIxLCBhbHBoYSA9IDAuOCkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgYWxwaGE9MC41KSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBhbHBoYT0wLjUpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdyZWVuIiwgInBpbmsiKSkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9ICJibGFjayIpICsgIAogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGNvb3JkX2ZpeGVkKFBDMl9ub291dGxpZXJzX25vUUMvUEMxX25vb3V0bGllcnNfbm9RQykgKwogICAgbGFicyh4ID0gZ2x1ZTo6Z2x1ZSgiUEMxOiB7UEMxX25vb3V0bGllcnNfbm9RQ30lIiksCiAgICAgICAgIHkgPSBnbHVlOjpnbHVlKCJQQzI6IHtQQzJfbm9vdXRsaWVyc19ub1FDfSUiKSwKICAgICAgICAgZmlsbCA9ICJwcmVfcG9zdCIsCiAgICAgICAgIHRpdGxlID0gIlByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzIFNjb3JlcyBQbG90IiwKICAgICAgICAgc3VidGl0bGUgPSAiTG9nMiB0cmFuc2Zvcm1lZCBkYXRhLCBubyA2MTA2IikpCmdncGxvdGx5KFBDQV9ub291dGxpZXJzX25vUUNzLk12c0YsCiAgICAgICAgIHRvb2x0aXAgPSAidGV4dCIpCmBgYAoKCgojIFBDQXRvb2xzIHBja2cKYGBge3IsZXZhbD1GQUxTRX0KICBpZiAoIXJlcXVpcmVOYW1lc3BhY2UoJ0Jpb2NNYW5hZ2VyJywgcXVpZXRseSA9IFRSVUUpKQogICAgaW5zdGFsbC5wYWNrYWdlcygnQmlvY01hbmFnZXInKQoKICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgnUENBdG9vbHMnKQpgYGAKCgpgYGB7cn0KbGlicmFyeShQQ0F0b29scykKYGBgCgojIyBXLyBPdXRsaWVycwoKIyMjIERhdGEgd3JhbmdsaW5nCmBgYHtyfQojIGNyZWF0ZSByZWwgYWJ1bmQgZGYgc3VpdGFibGUgZm9yIFBDQXRvb2xzIHBhY2thZ2UKCkRDX2ltcF9jbHVzdF9vbWljc2RhdGFfb3V0bGllcnNfZm9yUENBdG9vbHMgPC0gYXMuZGF0YS5mcmFtZSh0KERDX2ltcF9tZXRhYmluZF9jbHVzdF9sb2cyWywtYygyOjExKV0pKSAjIHRyYW5zcG9zZSBkZiAKCm5hbWVzKERDX2ltcF9jbHVzdF9vbWljc2RhdGFfb3V0bGllcnNfZm9yUENBdG9vbHMpIDwtIERDX2ltcF9jbHVzdF9vbWljc2RhdGFfb3V0bGllcnNfZm9yUENBdG9vbHNbMSxdICMgbWFrZSBzYW1wbGUgSURzIGNvbHVtbiBuYW1lcwoKRENfaW1wX2NsdXN0X29taWNzZGF0YV9vdXRsaWVyc19mb3JQQ0F0b29scyA8LSBEQ19pbXBfY2x1c3Rfb21pY3NkYXRhX291dGxpZXJzX2ZvclBDQXRvb2xzWy0xLF0gIyByZW1vdmUgc2FtcGxlIElEIHJvdwoKIyBjcmVhdGUgbWV0YWRhdGEgZGYgc3VpdGFibGUgZm9yIFBDQXRvb2xzIHBja2cKCm1ldGFkYXRhX291dGxpZXJzX2ZvclBDQXRvb2xzIDwtIG1ldGFkYXRhICU+JQogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAic2FtcGxlX0lEIikgIyBjaGFuZ2Ugc2FtcGxlIElEIGNvbHVtbiB0byByb3duYW1lcwoKIyBjcmVhdGUgYSB2ZWN0b3Igc28gdGhhdCBjb2wgbmFtZXMgaW4gYWJ1bmRhbmNlIGRmIG1hdGNoZXMgbWV0YWRhdGEgZGYKb3JkZXJfb3V0bGllcnNfZm9yUENBdG9vbHMgPC0gbWF0Y2gocm93bmFtZXMobWV0YWRhdGFfb3V0bGllcnNfZm9yUENBdG9vbHMpLCBjb2xuYW1lcyhEQ19pbXBfY2x1c3Rfb21pY3NkYXRhX291dGxpZXJzX2ZvclBDQXRvb2xzKSkKCiMgcmVvcmRlciBjb2wgbmFtZXMgaW4gYWJ1bmRhbmNlIGRmIHNvIHRoYXQgaXQgbWF0Y2hlcyBtZXRhZGF0YQphYnVuZGF0YV9vdXRsaWVyc19yZW9yZGVyZWRfZm9yUENBdG9vbHMgPC0gRENfaW1wX2NsdXN0X29taWNzZGF0YV9vdXRsaWVyc19mb3JQQ0F0b29sc1sgLG9yZGVyX291dGxpZXJzX2ZvclBDQXRvb2xzXSAKCiMgY2hhbmdlIGFidW5kYW5jZSBkZiB0byBudW1lcmljCmFidW5kYXRhX291dGxpZXJzX3Jlb3JkZXJlZF9mb3JQQ0F0b29scyA8LSBhYnVuZGF0YV9vdXRsaWVyc19yZW9yZGVyZWRfZm9yUENBdG9vbHMgJT4lCiAgbXV0YXRlX2FsbChhcy5udW1lcmljKQoKIyBMb2cgdHJhbnNmb3JtCmxvZzJfYWJ1bmRhdGFfb3V0bGllcnNfZm9yUENBdG9vbHMgPC0gbG9nMihhYnVuZGF0YV9vdXRsaWVyc19yZW9yZGVyZWRfZm9yUENBdG9vbHMpCgoKIyB1bml0ZSBwcmVfcG9zdCBjb2x1bW4gd2l0aCBpbnRlcnZlbnRpb24gY29sdW1uIHRvIGNyZWF0ZSBwcmVfaW50ZXJ2ZW50aW9uIGNvbHVtbgptZXRhZGF0YV9vdXRsaWVyc19mb3JQQ0F0b29scyA8LSBtZXRhZGF0YV9vdXRsaWVyc19mb3JQQ0F0b29scyAlPiUKICB1bml0ZShjb2wgPSAicHJlX3Bvc3RfaW50ZXJ2ZW50aW9uIiwKICAgICAgICBjKCJwcmVfcG9zdCIsIkludGVydmVudGlvbiIpLAogICAgICAgIHNlcCA9ICJfIiwKICAgICAgICByZW1vdmUgPSBGQUxTRSkKYGBgCgoKIyMjIFBDQQpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9Ny41fQojIHBjYQpwX291dGxpZXJzIDwtIFBDQXRvb2xzOjpwY2EobG9nMl9hYnVuZGF0YV9vdXRsaWVyc19mb3JQQ0F0b29scywgCiAgICAgICAgIG1ldGFkYXRhID0gbWV0YWRhdGFfb3V0bGllcnNfZm9yUENBdG9vbHMsIAogICAgICAgICBzY2FsZSA9IEZBTFNFICMgdXNpbmcgc2NhbGVkIGRhdGEgYWxyZWFkeSAobG9nMiB0cmFuc2Zvcm1lZCkKICAgICAgICAgCikKCiMgcGxvdAoKUENBdG9vbHM6OmJpcGxvdChwX291dGxpZXJzLAogICAgICAgICAgICAgICAgIHNob3dMb2FkaW5ncyA9IFRSVUUsICMgc2hvdyB2YXJpYWJsZXMgdGhhdCBjb250cmlidXRlIHRoZSBtb3N0IHRvIFBDcwogICAgICAgICAgICAgICAgIGxhYiA9IE5VTEwsCiAgICAgICAgICAgICAgICAgdGl0bGUgPSApCmBgYAoKCiMjIyBNb3JlIFBDQXMKCiMjIyMgUHJlIHZzIHBvc3QgYm90aAoKIyMjIyMgUEMxdlBDMgoKCmBgYHtyLCBmaWcud2lkdGg9OC41LCBmaWcuaGVpZ2h0PTYuNX0KICBiaXBsb3QocF9vdXRsaWVycywKICAgICAgICAgIGxhYiA9IHBhc3RlMChtZXRhZGF0YV9vdXRsaWVyc19mb3JQQ0F0b29scyRTdWJqZWN0KSwKICAgICAgICAgIGNvbGJ5ID0gJ3ByZV9wb3N0X2ludGVydmVudGlvbicsCiAgICAgICAgICBjb2xrZXkgPSBjKCJwcmVfWWVsbG93IiA9ICJ5ZWxsb3ciLAogICAgICAgICAgICAgICAgICAgICAicG9zdF9ZZWxsb3ciID0gInllbGxvdzQiLAogICAgICAgICAgICAgICAgICAgICAicHJlX1JlZCIgPSAicmVkIiwKICAgICAgICAgICAgICAgICAgICAgInBvc3RfUmVkIiA9ICJyZWQ0IiksCiAgICAgICAgICMgZWxsaXBzZSBjb25maWcKICAgICAgICAgZWxsaXBzZSA9IFRSVUUsCiAgICAgICAgIGVsbGlwc2VUeXBlID0gJ3QnLAogICAgICAgICBlbGxpcHNlTGV2ZWwgPSAwLjk1LAogICAgICAgICBlbGxpcHNlRmlsbCA9IFRSVUUsCiAgICAgICAgIGVsbGlwc2VBbHBoYSA9IDAuMiwKICAgICAgICAgZWxsaXBzZUxpbmVTaXplID0gMS4wLAogICAgICAgICB4bGltID0gYygtMjUsMjUpLCB5bGltID0gYygtMTAsIDEwKSwKICAgICAgICAgaGxpbmUgPSAwLCB2bGluZSA9IDAsCiAgICAgICAgIGxlZ2VuZFBvc2l0aW9uID0gJ3JpZ2h0JywKICAgICAgICAgdGl0bGUgPSAiUENBIFNjb3JlcyBQbG90IHdpdGggOTUlIENvbmZpZGVuY2UgSW50ZXJ2YWwiLAogICAgICAgICBzdWJ0aXRsZSA9ICJMb2cyIHRyYW5zZm9ybWVkIGRhdGEsIEhJTElDICgrKSwgd2l0aCBvdXRsaWVycywgbm8gUUNzIikKYGBgCgoKIyMgTm8gb3V0bGllcnMKCiMjIyBEYXRhIHdyYW5nbGluZwpgYGB7cn0KIyBjcmVhdGUgcmVsIGFidW5kIGRmIHN1aXRhYmxlIGZvciBQQ0F0b29scyBwYWNrYWdlCgpEQ19pbXBfY2x1c3Rfb21pY3NkYXRhX2ZvclBDQXRvb2xzIDwtIGFzLmRhdGEuZnJhbWUodChEQ19pbXBfbWV0YWJpbmRfY2x1c3RfbG9nMlssLWMoMjoxMSldKSkgIyB0cmFuc3Bvc2UgZGYgCgpuYW1lcyhEQ19pbXBfY2x1c3Rfb21pY3NkYXRhX2ZvclBDQXRvb2xzKSA8LSBEQ19pbXBfY2x1c3Rfb21pY3NkYXRhX2ZvclBDQXRvb2xzWzEsXSAjIG1ha2Ugc2FtcGxlIElEcyBjb2x1bW4gbmFtZXMKCkRDX2ltcF9jbHVzdF9vbWljc2RhdGFfZm9yUENBdG9vbHMgPC0gRENfaW1wX2NsdXN0X29taWNzZGF0YV9mb3JQQ0F0b29sc1stMSxdICMgcmVtb3ZlIHNhbXBsZSBJRCByb3cKCkRDX2ltcF9jbHVzdF9vbWljc2RhdGFfZm9yUENBdG9vbHMgPC0gRENfaW1wX2NsdXN0X29taWNzZGF0YV9mb3JQQ0F0b29scyAlPiUKICBkcGx5cjo6c2VsZWN0KCFjb250YWlucygiUUMiKSkgIyByZW1vdmUgUUMgb2JzZXJ2YXRpb25zCgoKIyBjcmVhdGUgbWV0YWRhdGEgZGYgc3VpdGFibGUgZm9yIFBDQXRvb2xzIHBja2cKCm1ldGFkYXRhX2ZvclBDQXRvb2xzIDwtIG1ldGFkYXRhICU+JQogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAic2FtcGxlX0lEIikgIyBjaGFuZ2Ugc2FtcGxlIElEIGNvbHVtbiB0byByb3duYW1lcwoKIyBjcmVhdGUgYSB2ZWN0b3Igc28gdGhhdCBjb2wgbmFtZXMgaW4gYWJ1bmRhbmNlIGRmIG1hdGNoZXMgbWV0YWRhdGEgZGYKb3JkZXJfZm9yUENBdG9vbHMgPC0gbWF0Y2gocm93bmFtZXMobWV0YWRhdGFfZm9yUENBdG9vbHMpLCBjb2xuYW1lcyhEQ19pbXBfY2x1c3Rfb21pY3NkYXRhX2ZvclBDQXRvb2xzKSkKCiMgcmVvcmRlciBjb2wgbmFtZXMgaW4gYWJ1bmRhbmNlIGRmIHNvIHRoYXQgaXQgbWF0Y2hlcyBtZXRhZGF0YQphYnVuZGF0YV9yZW9yZGVyZWRfZm9yUENBdG9vbHMgPC0gRENfaW1wX2NsdXN0X29taWNzZGF0YV9mb3JQQ0F0b29sc1sgLG9yZGVyX2ZvclBDQXRvb2xzXSAKCiMgY2hhbmdlIGFidW5kYW5jZSBkZiB0byBudW1lcmljCmFidW5kYXRhX3Jlb3JkZXJlZF9mb3JQQ0F0b29scyA8LSBhYnVuZGF0YV9yZW9yZGVyZWRfZm9yUENBdG9vbHMgJT4lCiAgbXV0YXRlX2FsbChhcy5udW1lcmljKQoKIyBMb2cgdHJhbnNmb3JtCmxvZzJfYWJ1bmRhdGFfZm9yUENBdG9vbHMgPC0gbG9nMihhYnVuZGF0YV9yZW9yZGVyZWRfZm9yUENBdG9vbHMpCgojIHJlbW92ZSBvdXRsaWVyIHN1YmogZnJvbSBib3RoIGRmCmxvZzJfYWJ1bmRhdGFfZm9yUENBdG9vbHMgPC0gbG9nMl9hYnVuZGF0YV9mb3JQQ0F0b29scyAlPiUKICBkcGx5cjo6c2VsZWN0KCFjb250YWlucygiNjEwNiIpKSAlPiUKICBkcGx5cjo6c2VsZWN0KCFjb250YWlucygiNjExMiIpKQoKbWV0YWRhdGFfZm9yUENBdG9vbHMgPC0gbWV0YWRhdGFfZm9yUENBdG9vbHMgJT4lCiAgZmlsdGVyKFN1YmplY3QgIT0gNjEwNiwKICAgICAgICAgU3ViamVjdCAhPSA2MTEyKQoKIyB1bml0ZSBwcmVfcG9zdCBjb2x1bW4gd2l0aCBpbnRlcnZlbnRpb24gY29sdW1uIHRvIGNyZWF0ZSBwcmVfaW50ZXJ2ZW50aW9uIGNvbHVtbgptZXRhZGF0YV9mb3JQQ0F0b29scyA8LSBtZXRhZGF0YV9mb3JQQ0F0b29scyAlPiUKICB1bml0ZShjb2wgPSAicHJlX3Bvc3RfaW50ZXJ2ZW50aW9uIiwKICAgICAgICBjKCJwcmVfcG9zdCIsIkludGVydmVudGlvbiIpLAogICAgICAgIHNlcCA9ICJfIiwKICAgICAgICByZW1vdmUgPSBGQUxTRSkKCmBgYAoKIyMjIFBDQQpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9Ny41fQojIHBjYQpwIDwtIFBDQXRvb2xzOjpwY2EobG9nMl9hYnVuZGF0YV9mb3JQQ0F0b29scywgCiAgICAgICAgIG1ldGFkYXRhID0gbWV0YWRhdGFfZm9yUENBdG9vbHMsIAogICAgICAgICBzY2FsZSA9IEZBTFNFICMgdXNpbmcgc2NhbGVkIGRhdGEgYWxyZWFkeSAobG9nMiB0cmFuc2Zvcm1lZCkKICAgICAgICAgCikKCiMgcGxvdAoKUENBdG9vbHM6OmJpcGxvdChwLAogICAgICAgICAgICAgICAgIHNob3dMb2FkaW5ncyA9IFRSVUUsICMgc2hvdyB2YXJpYWJsZXMgdGhhdCBjb250cmlidXRlIHRoZSBtb3N0IHRvIFBDcwogICAgICAgICAgICAgICAgIGxhYiA9IE5VTEwsCiAgICAgICAgICAgICAgICAgdGl0bGUgPSApCmBgYAoKIyMjIFNjcmVlcGxvdCBhbmFseXNpcwoKSG9ybidzIHBhcmFsbGVsIGFuYWx5c2lzCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpob3JuIDwtIHBhcmFsbGVsUENBKGxvZzJfYWJ1bmRhdGFfZm9yUENBdG9vbHMpCgpob3JuJG4KYGBgCgpFbGJvdyBtZXRob2QKYGBge3J9CmVsYm93IDwtIGZpbmRFbGJvd1BvaW50KHAkdmFyaWFuY2UpCgplbGJvdwpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9Ny41fQoKICBzY3JlZXBsb3QocCwKICAgIGNvbXBvbmVudHMgPSBnZXRDb21wb25lbnRzKHAsIDE6MjApLAogICAgdmxpbmUgPSBjKGhvcm4kbiwgZWxib3cpKSArCiAgZ2VvbV9sYWJlbChhZXMoeCA9IGhvcm4kbiArIDEsIHkgPSA1MCwKICAgICAgbGFiZWwgPSAnSG9yblwncycsIHZqdXN0ID0gLTEsIHNpemUgPSA4KSkgKwogICAgZ2VvbV9sYWJlbChhZXMoeCA9IGVsYm93ICsgMSwgeSA9IDUwLAogICAgICBsYWJlbCA9ICdFbGJvdyBtZXRob2QnLCB2anVzdCA9IC0zLCBzaXplID0gOCkpCmBgYAoKSG93IG1hbnkgUENzIGRvIHdlIG5lZWQgdG8gY2FwdHVyZSBhdCBsZWFzdCA4MCUgdmFyaWFuY2U/CmBgYHtyfQp3aGljaChjdW1zdW0ocCR2YXJpYW5jZSkgPiA4MClbMV0KYGBgCgojIyMgTW9yZSBQQ0FzCgojIyMjIFByZSB2cyBwb3N0IGJvdGgKCiMjIyMjIFBDMXZQQzIKCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpiaXBsb3QocCwKICAgICAgIGxhYiA9IHBhc3RlMChtZXRhZGF0YV9mb3JQQ0F0b29scyRTdWJqZWN0KSwKICAgICAgIGNvbGJ5ID0gJ3ByZV9wb3N0X2ludGVydmVudGlvbicsCiAgICAgICBjb2xrZXkgPSBjKCJwcmVfWWVsbG93IiA9ICJ5ZWxsb3ciLAogICAgICAgICAgICAgICAgICAicG9zdF9ZZWxsb3ciID0gInllbGxvdzQiLAogICAgICAgICAgICAgICAgICAicHJlX1JlZCIgPSAicmVkIiwKICAgICAgICAgICAgICAgICAgInBvc3RfUmVkIiA9ICJyZWQ0IiksCiAgICAgICBobGluZSA9IDAsIHZsaW5lID0gMCwKICAgICAgICMgZWxsaXBzZSBjb25maWcKICAgICAgIGVsbGlwc2UgPSBUUlVFLAogICAgICAgZWxsaXBzZVR5cGUgPSAndCcsICMgYXNzdW1lcyBtdWx0aXZhcmlhdGUgdC1kaXN0cmlidXRpb24KICAgICAgIGVsbGlwc2VMZXZlbCA9IDAuOTUsCiAgICAgICBlbGxpcHNlRmlsbCA9IFRSVUUsCiAgICAgICBlbGxpcHNlQWxwaGEgPSAwLjIsCiAgICAgICBlbGxpcHNlTGluZVNpemUgPSAwLAogICAgICAgeGxpbSA9IGMoLTcsNSksIHlsaW0gPSBjKC01LDUpLAogICAgICAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLAogICAgICAgdGl0bGUgPSAiUENBIFNjb3JlcyBQbG90IiwKICAgICAgIHN1YnRpdGxlID0gIkxvZzIgdHJhbnNmb3JtZWQgZGF0YSwgSElMSUMgKCspLCBvdXRsaWVycyByZW1vdmVkLCBubyBRQ3MgXG45NSUgY29uZmlkZW5jZSBsZXZlbCBlbGxpcHNlcyIpCgoKYGBgCgoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04LjV9CihQQ0EuY29sYnkucHJldnNwb3N0IDwtIGJpcGxvdChwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sYnkgPSAncHJlX3Bvc3RfaW50ZXJ2ZW50aW9uJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sa2V5ID0gYygicHJlX1llbGxvdyIgPSAieWVsbG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicG9zdF9ZZWxsb3ciID0gInllbGxvdzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwcmVfUmVkIiA9ICJyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwb3N0X1JlZCIgPSAicmVkNCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBobGluZSA9IDAsIHZsaW5lID0gMCwKICAgICAgICAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLAogICAgICAgICB0aXRsZSA9ICJQQ0EgU2NvcmVzIFBsb3QiLAogICAgICAgICBzdWJ0aXRsZSA9ICJMb2cyIHRyYW5zZm9ybWVkIGRhdGEsIEhJTElDICgrKSwgb3V0bGllcnMgcmVtb3ZlZCwgbm8gUUNzIFxuOTUlIGNvbmZpZGVuY2UgbGV2ZWwgZWxsaXBzZXMiLAogICAgICAgICBzaG93TG9hZGluZ3MgPSBUUlVFKSkKYGBgCgoKIyMjIyMgUGFpcnMgcGxvdApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwLCBtZXNzYWdlPUZBTFNFfQooUENBX3BhaXJzcGxvdC5jb2xieS5wcmV2c3Bvc3QgPC0KICBwYWlyc3Bsb3QocCwKICAgIGNvbXBvbmVudHMgPSBnZXRDb21wb25lbnRzKHAsIGMoMToxMCkpLAogICAgdHJpYW5nbGUgPSBUUlVFLCB0cmlhbmdsZWxhYlNpemUgPSAxMiwKICAgIGhsaW5lID0gMCwgdmxpbmUgPSAwLAogICAgcG9pbnRTaXplID0gMC40LAogICAgZ3JpZGxpbmVzLm1ham9yID0gRkFMU0UsIGdyaWRsaW5lcy5taW5vciA9IEZBTFNFLAogICAgY29sYnkgPSAncHJlX3Bvc3RfaW50ZXJ2ZW50aW9uJywgCiAgICBjb2xrZXkgPSBjKCJwcmVfWWVsbG93IiA9ICJ5ZWxsb3ciLAogICAgICAgICAgICAgICAicG9zdF9ZZWxsb3ciID0gInllbGxvdzQiLAogICAgICAgICAgICAgICAicHJlX1JlZCIgPSAicGluayIsCiAgICAgICAgICAgICAgICJwb3N0X1JlZCIgPSAicmVkNCIpLAogICAgdGl0bGUgPSAnUGFpcnMgcGxvdCcsIHBsb3RheGVzID0gRkFMU0UsCiAgICBtYXJnaW5nYXBzID0gdW5pdChjKC0wLjAxLCAtMC4wMSwgLTAuMDEsIC0wLjAxKSwgJ2NtJykpKQoKYGBgCgojIyMgU2V4CgojIyMjIFBDMXZQQzIKYGBge3IsIGZpZy53aWR0aD04LjUsIGZpZy5oZWlnaHQ9Ni41fQooUENBLmNvbGJ5LlNleCA8LSBiaXBsb3QocCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiID0gcGFzdGUwKG1ldGFkYXRhX2ZvclBDQXRvb2xzJFN1YmplY3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGJ5ID0gJ1NleCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sa2V5ID0gYygiTSIgPSAicmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGIiA9ICJwdXJwbGUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBobGluZSA9IDAsIHZsaW5lID0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9wb2ludChhZXModGV4dCA9IG1ldGFkYXRhX2ZvclBDQXRvb2xzJFN1YmplY3QpKSkpCgpnZ3Bsb3RseShQQ0EuY29sYnkuU2V4LAogICAgICAgICB0b29sdGlwID0gInRleHQiKSAKCmBgYAoKIyMjIyBQYWlyc3Bsb3QKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMCwgbWVzc2FnZT1GQUxTRX0KICBwYWlyc3Bsb3QocCwKICAgIGNvbXBvbmVudHMgPSBnZXRDb21wb25lbnRzKHAsIGMoMToxMCkpLAogICAgdHJpYW5nbGUgPSBUUlVFLCB0cmlhbmdsZWxhYlNpemUgPSAxMiwKICAgIGhsaW5lID0gMCwgdmxpbmUgPSAwLAogICAgcG9pbnRTaXplID0gMC40LAogICAgZ3JpZGxpbmVzLm1ham9yID0gRkFMU0UsIGdyaWRsaW5lcy5taW5vciA9IEZBTFNFLAogICAgY29sYnkgPSAnU2V4JywgCiAgICBjb2xrZXkgPSBjKCJNIiA9ICJyZWQiLAogICAgICAgICAgICAgICAiRiIgPSAicHVycGxlIiksCiAgICB0aXRsZSA9ICdQYWlycyBwbG90JywgcGxvdGF4ZXMgPSBGQUxTRSwKICAgIG1hcmdpbmdhcHMgPSB1bml0KGMoLTAuMDEsIC0wLjAxLCAtMC4wMSwgLTAuMDEpLCAnY20nKSkKYGBgCgoKIyMjIE92ZXJhbGwgcHJlIHYgcG9zdAoKIyMjIyBQQzF2UEMyCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02LjUsIG1lc3NhZ2U9RkFMU0V9CihQQ0EuY29sYnkub3ZlcmFsbC5wcmV2c3Bvc3QgPC0gYmlwbG90KHAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYiA9IHBhc3RlMChtZXRhZGF0YV9mb3JQQ0F0b29scyRTdWJqZWN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sYnkgPSAncHJlX3Bvc3QnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xrZXkgPSBjKCJwcmUiID0gIm9yYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBvc3QiID0gImdyZWVuMyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBobGluZSA9IDAsIHZsaW5lID0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW9tX3BvaW50KGFlcyh0ZXh0ID0gbWV0YWRhdGFfZm9yUENBdG9vbHMkU3ViamVjdCkpKSkKCmdncGxvdGx5KFBDQS5jb2xieS5vdmVyYWxsLnByZXZzcG9zdCwKICAgICAgICAgdG9vbHRpcCA9ICJ0ZXh0IikgCgpgYGAKCiMjIyMgUGFpcnNwbG90CgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwLCBtZXNzYWdlPUZBTFNFfQogIHBhaXJzcGxvdChwLAogICAgY29tcG9uZW50cyA9IGdldENvbXBvbmVudHMocCwgYygxOjEwKSksCiAgICB0cmlhbmdsZSA9IFRSVUUsIHRyaWFuZ2xlbGFiU2l6ZSA9IDEyLAogICAgaGxpbmUgPSAwLCB2bGluZSA9IDAsCiAgICBwb2ludFNpemUgPSAwLjQsCiAgICBncmlkbGluZXMubWFqb3IgPSBGQUxTRSwgZ3JpZGxpbmVzLm1pbm9yID0gRkFMU0UsCiAgICBjb2xieSA9ICdwcmVfcG9zdCcsIAogICAgY29sa2V5ID0gYygicHJlIiA9ICJvcmFuZ2UiLAogICAgICAgICAgICAgICAicG9zdCIgPSAiZ3JlZW4zIiksCiAgICB0aXRsZSA9ICdQYWlycyBwbG90JywgcGxvdGF4ZXMgPSBGQUxTRSwKICAgIG1hcmdpbmdhcHMgPSB1bml0KGMoLTAuMDEsIC0wLjAxLCAtMC4wMSwgLTAuMDEpLCAnY20nKSkKYGBgCgoKIyMjIFBlcmlvZAoKIyMjIyBQQzF2UEMyCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02LjUsIG1lc3NhZ2U9RkFMU0V9CihQQ0EuY29sYnkucGVyaW9kIDwtIGJpcGxvdChwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiID0gcGFzdGUwKG1ldGFkYXRhX2ZvclBDQXRvb2xzJFN1YmplY3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sYnkgPSAnUGVyaW9kJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhsaW5lID0gMCwgdmxpbmUgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9wb2ludChhZXModGV4dCA9IG1ldGFkYXRhX2ZvclBDQXRvb2xzJFN1YmplY3QpKSkpCgpnZ3Bsb3RseShQQ0EuY29sYnkucGVyaW9kLAogICAgICAgICB0b29sdGlwID0gInRleHQiKSAKCmBgYAoKIyMjIyBQYWlyc3Bsb3QKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMCwgbWVzc2FnZT1GQUxTRX0KICBwYWlyc3Bsb3QocCwKICAgIGNvbXBvbmVudHMgPSBnZXRDb21wb25lbnRzKHAsIGMoMToxMCkpLAogICAgdHJpYW5nbGUgPSBUUlVFLCB0cmlhbmdsZWxhYlNpemUgPSAxMiwKICAgIGhsaW5lID0gMCwgdmxpbmUgPSAwLAogICAgcG9pbnRTaXplID0gMC40LAogICAgZ3JpZGxpbmVzLm1ham9yID0gRkFMU0UsIGdyaWRsaW5lcy5taW5vciA9IEZBTFNFLAogICAgY29sYnkgPSAnUGVyaW9kJywKICAgIHRpdGxlID0gJ1BhaXJzIHBsb3QnLCBwbG90YXhlcyA9IEZBTFNFLAogICAgbWFyZ2luZ2FwcyA9IHVuaXQoYygtMC4wMSwgLTAuMDEsIC0wLjAxLCAtMC4wMSksICdjbScpKQpgYGAKCgoKIyMjIFNlcXVlbmNlCgojIyMjIFBDMXZQQzIKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYuNSwgbWVzc2FnZT1GQUxTRX0KKFBDQS5jb2xieS5zZXF1ZW5jZSA8LSBiaXBsb3QocCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYiA9IHBhc3RlMChtZXRhZGF0YV9mb3JQQ0F0b29scyRTdWJqZWN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGJ5ID0gJ3NlcXVlbmNlJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhsaW5lID0gMCwgdmxpbmUgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV9wb2ludChhZXModGV4dCA9IG1ldGFkYXRhX2ZvclBDQXRvb2xzJFN1YmplY3QpKSkpCgpnZ3Bsb3RseShQQ0EuY29sYnkuc2VxdWVuY2UsCiAgICAgICAgIHRvb2x0aXAgPSAidGV4dCIpIAoKYGBgCgojIyMjIFBhaXJzcGxvdApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwLCBtZXNzYWdlPUZBTFNFfQogIHBhaXJzcGxvdChwLAogICAgY29tcG9uZW50cyA9IGdldENvbXBvbmVudHMocCwgYygxOjEwKSksCiAgICB0cmlhbmdsZSA9IFRSVUUsIHRyaWFuZ2xlbGFiU2l6ZSA9IDEyLAogICAgaGxpbmUgPSAwLCB2bGluZSA9IDAsCiAgICBwb2ludFNpemUgPSAwLjQsCiAgICBncmlkbGluZXMubWFqb3IgPSBGQUxTRSwgZ3JpZGxpbmVzLm1pbm9yID0gRkFMU0UsCiAgICBjb2xieSA9ICdzZXF1ZW5jZScsCiAgICB0aXRsZSA9ICdQYWlycyBwbG90JywgcGxvdGF4ZXMgPSBGQUxTRSwKICAgIG1hcmdpbmdhcHMgPSB1bml0KGMoLTAuMDEsIC0wLjAxLCAtMC4wMSwgLTAuMDEpLCAnY20nKSkKYGBgCgojIyBFaWdlbiBjb3JwbG90cwpUaGlzIGlzIGEgY29vbCB3YXkgdG8gZXhwbG9yZSB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gdGhlIG1ldGFkYXRhIGFuZCB0aGUgUENzISBJIHdhbnQgdG8gbG9vayBhdCBob3cgdGhlIG1ldGF2YXJpYWJsZXMgY29ycmVsYXRlIHdpdGggUENzIHRoYXQgYWNjb3VudCBmb3IgODAlIHZhcmlhdGlvbiBpbiB0aGUgZGF0YXNldC4gCgpBZ2FpbjogSG93IG1hbnkgUENzIGRvIHdlIG5lZWQgdG8gY2FwdHVyZSBhdCBsZWFzdCA4MCUgdmFyaWFuY2U/CmBgYHtyfQp3aGljaChjdW1zdW0ocCR2YXJpYW5jZSkgPiA4MClbMV0KYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTcuNSwgbWVzc2FnZT1GQUxTRX0KCiAgZWlnZW5jb3JwbG90KHAsCiAgICBjb21wb25lbnRzID0gZ2V0Q29tcG9uZW50cyhwLCAxOjE1KSwgIyBnZXQgY29tcG9uZW50cyB0aGF0IGFjY291bnQgZm9yIDgwJSB2YXJpYW5jZQogICAgbWV0YXZhcnMgPSBjb2xuYW1lcyhtZXRhZGF0YV9mb3JQQ0F0b29scyksCiAgICBjb2wgPSBjKCdkYXJrYmx1ZScsICdibHVlMicsICdncmF5JywgJ3JlZDInLCAnZGFya3JlZCcpLAogICAgY2V4Q29ydmFsID0gMC43LAogICAgY29sQ29ydmFsID0gJ3doaXRlJywKICAgIGZvbnRDb3J2YWwgPSAyLAogICAgcG9zTGFiID0gJ2JvdHRvbWxlZnQnLAogICAgcm90TGFiWCA9IDQ1LAogICAgcG9zQ29sS2V5ID0gJ3RvcCcsCiAgICBjZXhMYWJDb2xLZXkgPSAxLjUsCiAgICBzY2FsZSA9IFRSVUUsCiAgICBtYWluID0gJ1BDMS0xNCBtZXRhZGF0YSBjb3JyZWxhdGlvbnMnLAogICAgY29sRnJhbWUgPSAnd2hpdGUnLAogICAgcGxvdFJzcXVhcmVkID0gRkFMU0UpCgoKYGBgCgoKYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD03LjUsIG1lc3NhZ2U9RkFMU0V9CiAgZWlnZW5jb3JwbG90KHAsCiAgICBjb21wb25lbnRzID0gZ2V0Q29tcG9uZW50cyhwLCAxOjE1KSwKICAgIG1ldGF2YXJzID0gY29sbmFtZXMobWV0YWRhdGFfZm9yUENBdG9vbHMpLAogICAgY29sID0gYygnd2hpdGUnLCAnY29ybnNpbGsxJywgJ2dvbGQnLCAnZm9yZXN0Z3JlZW4nLCAnZGFya2dyZWVuJyksCiAgICBjZXhDb3J2YWwgPSAxLjIsCiAgICBmb250Q29ydmFsID0gMiwKICAgIHBvc0xhYiA9ICdhbGwnLAogICAgcm90TGFiWCA9IDQ1LAogICAgc2NhbGUgPSBUUlVFLAogICAgbWFpbiA9IGJxdW90ZShQcmluY2lwYWwgfiBjb21wb25lbnQgfiBQZWFyc29uIH4gcl4yIH4gbWV0YWRhdGEgfiBjb3JyZWxhdGVzKSwKICAgIHBsb3RSc3F1YXJlZCA9IFRSVUUsCiAgICBjb3JGVU4gPSAncGVhcnNvbicsCiAgICBjb3JVU0UgPSAncGFpcndpc2UuY29tcGxldGUub2JzJywKICAgIGNvck11bHRpcGxlVGVzdENvcnJlY3Rpb24gPSAnQkgnLAogICAgc2lnbmlmU3ltYm9scyA9IGMoJyoqKionLCAnKioqJywgJyoqJywgJyonLCAnJyksCiAgICBzaWduaWZDdXRwb2ludHMgPSBjKDAsIDAuMDAwMSwgMC4wMDEsIDAuMDEsIDAuMDUsIDEpKQpgYGAKCgpJIGFtIG1vc3QgaW50ZXJlc3RlZCBpbiBQQ3MgYWZmZWN0ZWQgYnkgcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLCBzbyBJIHRoaW5rIGl0IHdvdWxkIGJlIGdvb2QgdG8gaW52ZXN0aWdhdGUgdGhlIG1ldGFib2xpdGVzIHRoYXQgY29udHJpYnV0ZSB0aGUgbW9zdCB0byB0aGVzZSBQQ3MuCgojIyBMb2FkaW5ncyBwbG90CmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CiMgbG9hZGluZ3MgcGxvdCBmb3IgUENzIDggYW5kIDkKICBwbG90bG9hZGluZ3MocCwKICAgIGNvbXBvbmVudHMgPSBnZXRDb21wb25lbnRzKHAsIGMoOCw5KSksCiAgICByYW5nZVJldGFpbiA9IDAuMSwgYWJzb2x1dGUgPSBUUlVFLAogICAgY29sID0gYygnYmxhY2snLCAncGluaycsICdyZWQ0JyksCiAgICBkcmF3Q29ubmVjdG9ycyA9IFRSVUUsIGxhYlNpemUgPSAzLAogICAgdGl0bGUgPSAiTG9hZGluZ3MgcGxvdCIsCiAgICBzdWJ0aXRsZSA9ICJQQyA4IGFuZCBQQyA5IiwKICAgIGNhcHRpb24gPSAiUHJlX3Bvc3RfaW50ZXJ2ZW50aW9uIGlzIGhpZ2hseSBjb3JyZWxhdGVkIHdpdGggdGhlc2UgUENzIikgKyBjb29yZF9mbGlwKCkKYGBgCgoKCiMgTXVsdGlsZXZlbCBQQ0EKCmBgYHtyfQpsaWJyYXJ5KG1peE9taWNzKQpgYGAKCgpgYGB7cn0KCkRhdGFfZm9yTVBDQSA8LSBEQ19pbXBfbWV0YWJpbmRfY2x1c3RfbG9nMl9ub1FDcyAlPiUKICBtdXRhdGVfYXQoIlN1YmplY3QiLCBhcy5mYWN0b3IpCiAKCnN1bW1hcnkoYXMuZmFjdG9yKERhdGFfZm9yTVBDQSRTdWJqZWN0KSkKCiMgbWFrZSBhIHZlY3RvciBmb3IgbWV0YSB2YXJpYWJsZXMKKG1ldGF2YXIgPC0gRGF0YV9mb3JNUENBWyxjKDE6MTEpXSAlPiUKICAgIGNvbG5hbWVzKCkpCmBgYAoKIyMgUENBIHcvIG91dGxpZXJzCmBgYHtyfQptaXhPbWljc1BDQS5yZXN1bHQgPC0gbWl4T21pY3M6OnBjYShEYXRhX2Zvck1QQ0FbLCFuYW1lcyhEYXRhX2Zvck1QQ0EpICVpbiUgbWV0YXZhcl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VudGVyID0gRkFMU0UpCgpwbG90SW5kaXYobWl4T21pY3NQQ0EucmVzdWx0LCAKICAgICAgICAgIGluZC5uYW1lcyA9IERhdGFfZm9yTVBDQSRTdWJqZWN0LCAKICAgICAgICAgIGdyb3VwID0gRGF0YV9mb3JNUENBJHByZV9wb3N0X2ludGVydmVudGlvbiwgCiAgICAgICAgICBsZWdlbmQgPSBUUlVFLCAKICAgICAgICAgIGxlZ2VuZC50aXRsZSA9ICJUcmVhdG1lbnQiLCAKICAgICAgICAgIHRpdGxlID0gJ1JlZ3VsYXIgUENBLCBISUxJQyAoKyksIExvZzIgdHJhbnNmb3JtZWQnKQoKYGBgCgoKIyMgTXVsdGlsZXZlbCBQQ0EKV2l0aCBhbGwgZGF0YQoKIyMjIHByZV9wb3N0X2ludGVydmVudGlvbgpgYGB7cn0KbXVsdGlsZXZlbFBDQS5yZXN1bHQgPC0gbWl4T21pY3M6OnBjYShEYXRhX2Zvck1QQ0FbLC0oYygxOjExKSldLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11bHRpbGV2ZWwgPSBEYXRhX2Zvck1QQ0EkU3ViamVjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZW50ZXIgPSBGQUxTRSkKCnBsb3RJbmRpdihtdWx0aWxldmVsUENBLnJlc3VsdCwgCiAgICAgICAgICBpbmQubmFtZXMgPSBEYXRhX2Zvck1QQ0EkU3ViamVjdCwgCiAgICAgICAgICBncm91cCA9IERhdGFfZm9yTVBDQSRwcmVfcG9zdF9pbnRlcnZlbnRpb24sIAogICAgICAgICAgbGVnZW5kID0gVFJVRSwgCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSAiVHJlYXRtZW50IiwgCiAgICAgICAgICB0aXRsZSA9ICdNdWx0aWxldmVsIFBDQSwgSElMSUMgKCspLCBMb2cyIHRyYW5zZm9ybWVkJykKCmBgYAoKIyMjIyBMb2FkaW5ncyAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMn0KcGxvdExvYWRpbmdzKG11bHRpbGV2ZWxQQ0EucmVzdWx0LCBuZGlzcGxheSA9IDc1KQpgYGAKCgojIyMgcHJlX3Bvc3QKYGBge3J9Cm11bHRpbGV2ZWxQQ0EucmVzdWx0IDwtIG1peE9taWNzOjpwY2EoRGF0YV9mb3JNUENBWywtKGMoMToxMSkpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdWx0aWxldmVsID0gRGF0YV9mb3JNUENBJFN1YmplY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2VudGVyID0gRkFMU0UpCgpwbG90SW5kaXYobXVsdGlsZXZlbFBDQS5yZXN1bHQsIAogICAgICAgICAgaW5kLm5hbWVzID0gRGF0YV9mb3JNUENBJFN1YmplY3QsIAogICAgICAgICAgZ3JvdXAgPSBEYXRhX2Zvck1QQ0EkcHJlX3Bvc3QsIAogICAgICAgICAgbGVnZW5kID0gVFJVRSwgCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSAiVHJlYXRtZW50IiwgCiAgICAgICAgICB0aXRsZSA9ICdNdWx0aWxldmVsIFBDQSwgSElMSUMgKCspLCBMb2cyIHRyYW5zZm9ybWVkJykKCmBgYAoKIyMjIyBMb2FkaW5ncyAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMn0KcGxvdExvYWRpbmdzKG11bHRpbGV2ZWxQQ0EucmVzdWx0LCBuZGlzcGxheSA9IDc1LCBjb21wID0gMikKCmBgYAoKCiMgVW5pdmFyaWF0ZSBhbmFseXNpcwoKIyMgV3JhbmdsZSBkYXRhCgp1c2UgdGlkeSBkYXRhCmBgYHtyfQojIHRpZHkgZGYgCkRDX2ltcF9tZXRhYmluZF9jbHVzdF90aWR5X2xvZzIgPC0gRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzIgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAxMjpuY29sKC4pLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJtel9ydCIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJyZWxfYWJ1bmRfbG9nMiIpCgojIHVzZSB0aWR5IGRhdGEgCmhlYWQoRENfaW1wX21ldGFiaW5kX2NsdXN0X3RpZHlfbG9nMikKYGBgCgpgYGB7cn0KCiMgcmVtb3ZlIFFDcwpkZl9mb3Jfc3RhdHMgPC0gRENfaW1wX21ldGFiaW5kX2NsdXN0X3RpZHlfbG9nMiAlPiUKICBmaWx0ZXIoSW50ZXJ2ZW50aW9uICE9ICJRQyIpCgojIGNoZWNrIGlmIFFDcyB3ZXJlIHJlbW92ZWQKdW5pcXVlKGRmX2Zvcl9zdGF0cyRJbnRlcnZlbnRpb24pCmBgYAoKYGBge3J9CiMgZGYgd2l0aG91dCBvdXRsaWVycwpkZl9mb3Jfc3RhdHNfbm9PdXRsaWVyIDwtIGRmX2Zvcl9zdGF0cyAlPiUKICBmaWx0ZXIoU3ViamVjdCAhPSAiNjEwNiIsCiAgICAgICAgIFN1YmplY3QgIT0gIjYxMTIiKQoKIyBjaGVjayBpZiBvdXRsaWVyIHdhcyByZW1vdmVkCnVuaXF1ZShkZl9mb3Jfc3RhdHNfbm9PdXRsaWVyJFN1YmplY3QpCmBgYAoKCmBgYHtyfQojIHR1cm4gb2ZmIHNjaSBub3RhdGlvbiBvdXRwdXRzCm9wdGlvbnMoc2NpcGVuID0gOTk5KQpgYGAKCgojIyBQYXJhbWV0cmljIHRlc3RzCgojIyMgQU5PVkEgKHJlcGVhdGVkIG1lYXN1cmVzKSBhY3Jvc3MgdGltZXBvaW50cwoKYGBge3J9Cgphbm92YV9vdXRwb3V0X2RmIDwtIGRmX2Zvcl9zdGF0c19ub091dGxpZXIgJT4lCiAgZHBseXI6OnNlbGVjdChTdWJqZWN0LCBwcmVfcG9zdF9pbnRlcnZlbnRpb24sIG16X3J0LCByZWxfYWJ1bmRfbG9nMikgJT4lCiAgZ3JvdXBfYnkobXpfcnQpICU+JQogIGFub3ZhX3Rlc3QocmVsX2FidW5kX2xvZzIgfiBwcmVfcG9zdF9pbnRlcnZlbnRpb24sIHdpZCA9IFN1YmplY3QsCiAgICAgICAgICAgICBkZXRhaWxlZCA9IFRSVUUpICU+JQogIGFkanVzdF9wdmFsdWUobWV0aG9kID0gIkJIIikgJT4lCiAgYXMuZGF0YS5mcmFtZSgpCgphbm92YV9zaWcgPC0gYW5vdmFfb3V0cG91dF9kZiAlPiUKICBmaWx0ZXIocC5hZGogPD0gMC4wNSkKCiMgaG93IG1hbnkgc2lnbmlmaWNhbnQgZmVhdHVyZXM/Cm5yb3coYW5vdmFfc2lnKQogIApgYGAKCiMjIyMgSGVhdG1hcCBvZiBmZWF0dXJlcyBzaWduaWZpY2FudCBieSBBTk9WQQpgYGB7ciwgZmlnLmhlaWdodD0xMywgZmlnLmFzcD0xfQpBTk9WQV9oZWF0bWFwX2RhdGEgPC0gRENfaW1wX21ldGFiaW5kX2NsdXN0X2xvZzJfbm9RQ3MgJT4lCiAgdW5pdGUoIlN1YmplY3RfcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uIiwgU3ViamVjdCwgcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEZBTFNFKSAlPiUKICBkcGx5cjo6c2VsZWN0KFN1YmplY3QsIFN1YmplY3RfcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLCBwcmVfcG9zdCwgYWxsX29mKGFub3ZhX3NpZyRtel9ydCkpICU+JQogICMgcmVtb3ZlIG91dGxpZXJzCiAgZmlsdGVyKFN1YmplY3QgIT0gNjEwNiwKICAgICAgICAgU3ViamVjdCAhPSA2MTEyKQoKQU5PVkFfaGVhdG1hcCA8LSAKICBwaGVhdG1hcChBTk9WQV9oZWF0bWFwX2RhdGFbLC1jKDE6MyldLAogICAgICAgICAgIHNjYWxlID0gImNvbHVtbiIsCiAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwKICAgICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIiwKICAgICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiZXVjbGlkZWFuIiwKICAgICAgICAgICBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIiwKICAgICAgICAgICBsYWJlbHNfcm93ID0gQU5PVkFfaGVhdG1hcF9kYXRhJFN1YmplY3RfcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLAogICAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCIjNjdhOWNmIiwgIiNmN2Y3ZjciLCAiI2VmOGE2MiIpKSgxNiksCiAgICAgICAgICAgbWFpbiA9ICJIZWF0bWFwIG9mIGZlYXR1cmVzIHNpZ25pZmljYW50IGFjcm9zcyB0aW1lcG9pbnRzIFxuYnkgcmVwZWF0ZWQgbWVhc3VyZXMgb25lLXdheSBBTk9WQSBcbkJlbmphbW9uaS1Ib2NoYmVyZyBjb3JyZWN0ZWQgcC12YWx1ZXMgPiAwLjA1IFxuSElMSUMgKCspIikKICAKYGBgCgpgYGB7cn0KZ2dzYXZlKHBsb3QgPSBBTk9WQV9oZWF0bWFwLCBoZWlnaHQgPSAxMywKICAgICAgIGZpbGVuYW1lID0gInBsb3RzIGFuZCBmaWd1cmVzL0FOT1ZBX3NpZ19oZWF0bWFwLnN2ZyIpCmBgYAoKIyMjIFBhaXJlZCB0IHRlc3RzCgpIZXJlLCBJIGFtIGNvbXBhcmluZyBwcmUtIHRvIHBvc3QtaW50ZXJ2ZW50aW9uIGZvciBib3RoIHllbGxvdyBhbmQgdG9tYXRvIHNveSAoUmVkKSBqdWljZSBpbnRlcnZlbnRpb25zLiBJIGFsc28gY29tcGFyZSBwb3N0LXllbGxvdyB0byBwb3N0LXJlZCBpbnRlcnZlbnRpb24uIEkgYW0gdXNpbmcgdGhlIGxvZyB0cmFuc2Zvcm1lZCB2YWx1ZXMgb2YgcmVsIGFidW5kYW5jZSBzaW5jZSBwYXJhbWV0cmljIHRlc3RzIGFzc3VtZSBub3JtYWxpdHkuCgojIyMjIEN0cmwKYGBge3J9CiMgcnVuIHBhaXJlZCB0LXRlc3RzIGZvciBjb250cm9sIGludGVydmVudGlvbgpjdHJsX3QudGVzdF9wYWlyZWQgPC0gZGZfZm9yX3N0YXRzICU+JQogIGZpbHRlcihJbnRlcnZlbnRpb24gPT0gIlllbGxvdyIpICU+JQogIGRwbHlyOjpzZWxlY3QoU3ViamVjdCwgcHJlX3Bvc3QsIG16X3J0LCByZWxfYWJ1bmRfbG9nMikgJT4lCiAgZ3JvdXBfYnkobXpfcnQpICU+JQogIHRfdGVzdChyZWxfYWJ1bmRfbG9nMiB+IHByZV9wb3N0LCAKICAgICAgICAgcGFpcmVkID0gVFJVRSwgCiAgICAgICAgIHAuYWRqdXN0Lm1ldGhvZCA9ICJCSCIpICU+JSAjIEJlbmphbWluaS1Ib2NoYmVyZyBjb250cm9sbGluZyB0byBsb3dlciBmYWxzZSBwb3NpdGl2ZXMKICBhZGRfc2lnbmlmaWNhbmNlKCkKYGBgCgpTdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGZlYXR1cmVzCmBgYHtyfQojIHdoaWNoIGZlYXR1cmVzIGFyZSBzaWduaWZpY2FudD8KY3RybF90LnRlc3RfcGFpcmVkX3NpZyA8LSBjdHJsX3QudGVzdF9wYWlyZWQgJT4lCiAgZmlsdGVyKHAgPD0gMC4wNSkKdGliYmxlKGN0cmxfdC50ZXN0X3BhaXJlZF9zaWcpCgojIGhvdyBtYW55IGFyZSBzaWduaWZpY2FudD8KbnJvdyhjdHJsX3QudGVzdF9wYWlyZWRfc2lnKQpgYGAKCgoKIyMjIyBDdHJsIG5vIG91dGxpZXIKYGBge3J9CiMgcnVuIHBhaXJlZCB0LXRlc3RzIGZvciBjb250cm9sIGludGVydmVudGlvbgpjdHJsX25vT3V0bGllcl90LnRlc3RfcGFpcmVkIDwtIGRmX2Zvcl9zdGF0c19ub091dGxpZXIgJT4lCiAgZmlsdGVyKEludGVydmVudGlvbiA9PSAiWWVsbG93IikgJT4lCiAgZHBseXI6OnNlbGVjdChTdWJqZWN0LCBwcmVfcG9zdCwgbXpfcnQsIHJlbF9hYnVuZF9sb2cyKSAlPiUKICBncm91cF9ieShtel9ydCkgJT4lCiAgdF90ZXN0KHJlbF9hYnVuZF9sb2cyIH4gcHJlX3Bvc3QsIAogICAgICAgICBwYWlyZWQgPSBUUlVFLCAKICAgICAgICAgcC5hZGp1c3QubWV0aG9kID0gIkJIIikgJT4lICMgQmVuamFtaW5pLUhvY2hiZXJnIGNvbnRyb2xsaW5nIHRvIGxvd2VyIGZhbHNlIHBvc2l0aXZlcwogIGFkZF9zaWduaWZpY2FuY2UoKQpgYGAKClN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZmVhdHVyZXMKYGBge3J9CiMgd2hpY2ggZmVhdHVyZXMgYXJlIHNpZ25pZmljYW50PwpjdHJsX25vT3V0bGllcl90LnRlc3RfcGFpcmVkX3NpZyA8LSBjdHJsX25vT3V0bGllcl90LnRlc3RfcGFpcmVkICU+JQogIGZpbHRlcihwIDw9IDAuMDUpCnRpYmJsZShjdHJsX25vT3V0bGllcl90LnRlc3RfcGFpcmVkX3NpZykKCiMgaG93IG1hbnkgYXJlIHNpZ25pZmljYW50Pwpucm93KGN0cmxfbm9PdXRsaWVyX3QudGVzdF9wYWlyZWRfc2lnKQpgYGAKCiMjIyMjIE11bW1pY2hvZyBsaXN0CmBgYHtyfQp5ZWxsb3dfZm9yX211bW1pY2hvZyA8LSBjdHJsX25vT3V0bGllcl90LnRlc3RfcGFpcmVkICU+JQogIGRwbHlyOjpzZWxlY3QobXpfcnQsCiAgICAgICAgIHAsCiAgICAgICAgIHN0YXRpc3RpYykgJT4lCiAgc2VwYXJhdGUoY29sID0gbXpfcnQsCiAgICAgICAgICAgaW50byA9IGMoIm0ueiIsICJyLnQuIiksCiAgICAgICAgICAgc2VwID0gIl8iKSAlPiUKICByZW5hbWUoInQuc2NvcmUiID0gInN0YXRpc3RpYyIpCgp3cml0ZV9jc3YoeWVsbG93X2Zvcl9tdW1taWNob2csCiAgICAgICAgICAieWVsLW11bW1pY2hvZy1saXN0LWhpbGljcG9zLmNzdiIpCgpgYGAKCiMjIyMgUmVkCmBgYHtyfQojIHJ1biBwYWlyZWQgdC10ZXN0cyBmb3IgY29udHJvbCBpbnRlcnZlbnRpb24KcmVkX3QudGVzdF9wYWlyZWQgPC0gZGZfZm9yX3N0YXRzICU+JQogIGZpbHRlcihJbnRlcnZlbnRpb24gPT0gIlJlZCIpICU+JQogIGRwbHlyOjpzZWxlY3QoU3ViamVjdCwgcHJlX3Bvc3QsIG16X3J0LCByZWxfYWJ1bmRfbG9nMikgJT4lCiAgZ3JvdXBfYnkobXpfcnQpICU+JQogIHRfdGVzdChyZWxfYWJ1bmRfbG9nMiB+IHByZV9wb3N0LCAKICAgICAgICAgcGFpcmVkID0gVFJVRSwgCiAgICAgICAgIHAuYWRqdXN0Lm1ldGhvZCA9ICJCSCIpICU+JSAjIEJlbmphbWluaS1Ib2NoYmVyZyBjb250cm9sbGluZyB0byBsb3dlciBmYWxzZSBwb3NpdGl2ZXMKICBhZGRfc2lnbmlmaWNhbmNlKCkKYGBgCgpTdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGZlYXR1cmVzCmBgYHtyfQojIHdoaWNoIGZlYXR1cmVzIGFyZSBzaWduaWZpY2FudD8KcmVkX3QudGVzdF9wYWlyZWRfc2lnIDwtIHJlZF90LnRlc3RfcGFpcmVkICU+JQogIGZpbHRlcihwIDw9IDAuMDUpCnRpYmJsZShyZWRfdC50ZXN0X3BhaXJlZF9zaWcpCgojIGhvdyBtYW55IGFyZSBzaWduaWZpY2FudD8KbnJvdyhyZWRfdC50ZXN0X3BhaXJlZF9zaWcpCmBgYAoKCiMjIyMgUmVkIG5vIG91dGxpZXIKYGBge3J9CiMgcnVuIHBhaXJlZCB0LXRlc3RzIGZvciBjb250cm9sIGludGVydmVudGlvbgpyZWRfbm9PdXRsaWVyX3QudGVzdF9wYWlyZWQgPC0gZGZfZm9yX3N0YXRzX25vT3V0bGllciAlPiUKICBmaWx0ZXIoSW50ZXJ2ZW50aW9uID09ICJSZWQiKSAlPiUKICBkcGx5cjo6c2VsZWN0KFN1YmplY3QsIHByZV9wb3N0LCBtel9ydCwgcmVsX2FidW5kX2xvZzIpICU+JQogIGdyb3VwX2J5KG16X3J0KSAlPiUKICB0X3Rlc3QocmVsX2FidW5kX2xvZzIgfiBwcmVfcG9zdCwgCiAgICAgICAgIHBhaXJlZCA9IFRSVUUsIAogICAgICAgICBwLmFkanVzdC5tZXRob2QgPSAiQkgiKSAlPiUgIyBCZW5qYW1pbmktSG9jaGJlcmcgY29udHJvbGxpbmcgdG8gbG93ZXIgZmFsc2UgcG9zaXRpdmVzCiAgYWRkX3NpZ25pZmljYW5jZSgpCmBgYAoKU3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBmZWF0dXJlcwpgYGB7cn0KIyB3aGljaCBmZWF0dXJlcyBhcmUgc2lnbmlmaWNhbnQ/CnJlZF9ub091dGxpZXJfdC50ZXN0X3BhaXJlZF9zaWcgPC0gcmVkX25vT3V0bGllcl90LnRlc3RfcGFpcmVkICU+JQogIGZpbHRlcihwIDw9IDAuMDUpCnRpYmJsZShyZWRfbm9PdXRsaWVyX3QudGVzdF9wYWlyZWRfc2lnKQoKIyBob3cgbWFueSBhcmUgc2lnbmlmaWNhbnQ/Cm5yb3cocmVkX25vT3V0bGllcl90LnRlc3RfcGFpcmVkX3NpZykKYGBgCgoKIyMjIyMgTXVtbWljaG9nIGxpc3QKYGBge3J9CnJlZF9mb3JfbXVtbWljaG9nIDwtIHJlZF9ub091dGxpZXJfdC50ZXN0X3BhaXJlZCAlPiUKICBkcGx5cjo6c2VsZWN0KG16X3J0LAogICAgICAgICBwLAogICAgICAgICBzdGF0aXN0aWMpICU+JQogIHNlcGFyYXRlKGNvbCA9IG16X3J0LAogICAgICAgICAgIGludG8gPSBjKCJtLnoiLCAici50LiIpLAogICAgICAgICAgIHNlcCA9ICJfIikgJT4lCiAgcmVuYW1lKCJ0LnNjb3JlIiA9ICJzdGF0aXN0aWMiKQoKd3JpdGVfY3N2KHJlZF9mb3JfbXVtbWljaG9nLAogICAgICAgICAgInJlZC1tdW1taWNob2ctbGlzdC1oaWxpY3Bvcy5jc3YiKQpgYGAKCgoKIyMjIyBQb3N0LXJlZCB2cyBwb3N0LXllbGxvdwoKYGBge3J9CiMgcnVuIHBhaXJlZCB0LXRlc3RzIGZvciBwb3N0IGludGVydmVudGlvbnMKcG9zdF90LnRlc3RfcGFpcmVkIDwtIGRmX2Zvcl9zdGF0cyAlPiUKICBmaWx0ZXIocHJlX3Bvc3QgPT0gInBvc3QiKSAlPiUKICBkcGx5cjo6c2VsZWN0KFN1YmplY3QsIEludGVydmVudGlvbiwgbXpfcnQsIHJlbF9hYnVuZF9sb2cyKSAlPiUKICBncm91cF9ieShtel9ydCkgJT4lCiAgdF90ZXN0KHJlbF9hYnVuZF9sb2cyIH4gSW50ZXJ2ZW50aW9uLCAKICAgICAgICAgcGFpcmVkID0gVFJVRSwgCiAgICAgICAgIHAuYWRqdXN0Lm1ldGhvZCA9ICJCSCIpICU+JSAjIEJlbmphbWluaS1Ib2NoYmVyZyBjb250cm9sbGluZyB0byBsb3dlciBmYWxzZSBwb3NpdGl2ZXMKICBhZGRfc2lnbmlmaWNhbmNlKCkKYGBgCgpTdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGZlYXR1cmVzCmBgYHtyfQojIHdoaWNoIGZlYXR1cmVzIGFyZSBzaWduaWZpY2FudD8KcG9zdF90LnRlc3RfcGFpcmVkX3NpZyA8LSBwb3N0X3QudGVzdF9wYWlyZWQgJT4lCiAgZmlsdGVyKHAgPD0gMC4wNSkKdGliYmxlKHBvc3RfdC50ZXN0X3BhaXJlZF9zaWcpCgojIGhvdyBtYW55IGFyZSBzaWduaWZpY2FudD8KbnJvdyhwb3N0X3QudGVzdF9wYWlyZWRfc2lnKQpgYGAKCiMjIyMgUG9zdC1yZWQgdnMgcG9zdC15ZWxsb3cgbm8gT3V0bGllcgoKYGBge3J9CiMgcnVuIHBhaXJlZCB0LXRlc3RzIGZvciBwb3N0IGludGVydmVudGlvbnMKcG9zdF9ub091dGxpZXJfdC50ZXN0X3BhaXJlZCA8LSBkZl9mb3Jfc3RhdHMgJT4lCiAgZmlsdGVyKHByZV9wb3N0ID09ICJwb3N0IiwKICAgICAgICAgU3ViamVjdCAhPSAiNjEwNiIpICU+JQogIGRwbHlyOjpzZWxlY3QoU3ViamVjdCwgSW50ZXJ2ZW50aW9uLCBtel9ydCwgcmVsX2FidW5kX2xvZzIpICU+JQogIGdyb3VwX2J5KG16X3J0KSAlPiUKICB0X3Rlc3QocmVsX2FidW5kX2xvZzIgfiBJbnRlcnZlbnRpb24sIAogICAgICAgICBwYWlyZWQgPSBUUlVFLCAKICAgICAgICAgcC5hZGp1c3QubWV0aG9kID0gIkJIIikgJT4lICMgQmVuamFtaW5pLUhvY2hiZXJnIGNvbnRyb2xsaW5nIHRvIGxvd2VyIGZhbHNlIHBvc2l0aXZlcwogIGFkZF9zaWduaWZpY2FuY2UoKQpgYGAKClN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZmVhdHVyZXMKYGBge3J9CiMgd2hpY2ggZmVhdHVyZXMgYXJlIHNpZ25pZmljYW50Pwpwb3N0X25vT3V0bGllcl90LnRlc3RfcGFpcmVkX3NpZyA8LSBwb3N0X25vT3V0bGllcl90LnRlc3RfcGFpcmVkICU+JQogIGZpbHRlcihwIDw9IDAuMDUpCnRpYmJsZShwb3N0X25vT3V0bGllcl90LnRlc3RfcGFpcmVkX3NpZykKCiMgaG93IG1hbnkgYXJlIHNpZ25pZmljYW50Pwpucm93KHBvc3Rfbm9PdXRsaWVyX3QudGVzdF9wYWlyZWRfc2lnKQpgYGAKCiMjIyMjIE11bW1pY2hvZyBsaXN0CmBgYHtyfQpwb3N0X2Zvcl9tdW1taWNob2cgPC0gcG9zdF9ub091dGxpZXJfdC50ZXN0X3BhaXJlZCAlPiUKICBkcGx5cjo6c2VsZWN0KG16X3J0LAogICAgICAgICBwLAogICAgICAgICBzdGF0aXN0aWMpICU+JQogIHNlcGFyYXRlKGNvbCA9IG16X3J0LAogICAgICAgICAgIGludG8gPSBjKCJtLnoiLCAici50IiksCiAgICAgICAgICAgc2VwID0gIl8iKSAlPiUKICByZW5hbWUoInAudmFsdWUiID0gInAiKSAlPiUKICByZW5hbWUoInQuc2NvcmUiID0gInN0YXRpc3RpYyIpCgp3cml0ZV9jc3YocG9zdF9mb3JfbXVtbWljaG9nLAogICAgICAgICAgInBvc3QtbXVtbWljaG9nLWxpc3QtaGlsaWNwb3MuY3N2IikKCmBgYAoKCiMjIyMgT3V0bGllciBjb21wYXJpc29uCkFyZSB0aGVyZSBhbnkgc2lnbmlmaWNhbnQgZmVhdHVyZXMgc2hhcmVkIGJldHdlZW4gdGVzdHMgd2l0aCBhbmQgd2l0aG91dCBvdXRsaWVyPwoKYGBge3J9CnBvc3Rfc2lnX291dGxpZXJfY29tcCA8LSBsaXN0KHBvc3Rfbm9PdXRsaWVyX3QudGVzdF9wYWlyZWRfc2lnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3N0X3QudGVzdF9wYWlyZWRfc2lnKSAlPiUKICByZWR1Y2UoaW5uZXJfam9pbiwgYnkgPSAibXpfcnQiKQoKdGliYmxlKHBvc3Rfc2lnX291dGxpZXJfY29tcCkKIyBob3cgbWFueSBzaWcgZmVhdHVyZXMgYXJlIHNoYXJlZCBiZXR3ZWVuIGRmIHZzIGRmIHcvbyBvdXRsaWVycwpucm93KHBvc3Rfc2lnX291dGxpZXJfY29tcCkKCiMgcmV0dXJuIHNpZyBmZWF0dXJlcyBwcmVzZW50IG9ubHkgaW4gZGYgd2l0aCBvdXRsaWVyLCBhbmQgbm90IGluIGRmIHdpdGhvdXQgb3V0bGllcgp0aWJibGUoYW50aV9qb2luKHBvc3Rfbm9PdXRsaWVyX3QudGVzdF9wYWlyZWRfc2lnLAogICAgICAgICAgcG9zdF90LnRlc3RfcGFpcmVkX3NpZywKICAgICAgICAgIGJ5ID0gIm16X3J0IikpCgojIHJldHVybiBzaWcgZmVhdHVyZXMgZnJvbSBkZiB3aXRob3V0IG91dGxpZXIgdGhhdCBhcmUgYWxzbyBwcmVzZW50IGluIGRmIHdpdGggb3V0bGllcgprYWJsZShzZW1pX2pvaW4ocG9zdF9ub091dGxpZXJfdC50ZXN0X3BhaXJlZF9zaWcsCiAgICAgICAgICBwb3N0X3QudGVzdF9wYWlyZWRfc2lnLAogICAgICAgICAgYnkgPSAibXpfcnQiKSkKYGBgCgojIyMgU3RhbmRhcmQgY29tcGFyaXNvbnMKCkhlcmUsIEkgd2FudCB0byBvbmx5IGZvY3VzIG9uIHRoZSBtZXRhYm9saXRlcyB0aGF0IEkgaHlwb3RoZXNpemVkIHRvIGNoYW5nZS4gVGhpcyB3YXkgSSBjYW4gYXZvaWQgbXVsdGlwbGUgY29ycmVjdGlvbiBhZGp1c3RtZW50cyBhbmQgc2VlIGlmIEkgY2FuIGNhcHR1cmUgYW55IHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGF0IGRpZmZlcmVudCB0aW1lcG9pbnRzCgojIyMjIFdyYW5nbGUKCmBgYHtyfQpzdGRzX2RmX2Zvcl9zdGF0c193aWRlIDwtIGRmX2Zvcl9zdGF0cyAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbXpfcnQsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSByZWxfYWJ1bmRfbG9nMikgJT4lCiAgZHBseXI6OnNlbGVjdChjKDE6MTEpLAogICAgICAgICAgICAgICAgIjExOC4wODY3XzQuNjc4IiwgI3ZhbGluZQogICAgICAgICAgICAgICAgIjE0Ny4wNzY1XzYuNTgyIiwgI2dsdXRhbWluZQogICAgICAgICAgICAgICAgIjE2Mi4xMTI5XzUuNjUxIiwgI2wtY2Fybml0aW5lCiAgICAgICAgICAgICAgICAiMTY2LjA4NjFfNC40MDQiLCAjcGhlbnlsYWxhbmluZQogICAgICAgICAgICAgICAgIjE4Mi4wODA4XzUuNjQ3IiwgI3R5cm9zaW5lCiAgICAgICAgICAgICAgICAiMTQ3LjExMjlfOC4yNTEiLCAjbHlzaW5lCiAgICAgICAgICAgICAgICAiMTU2LjA3NzJfNy42NjkiLCAjaGlzdGlkaW5lCiAgICAgICAgICAgICAgICAiMjA1LjA5NzJfNC43NTIiLCAjdHJ5cHRvcGhhbgogICAgICAgICAgICAgICAgIjE3NS4xMTlfOC4xMDYiLCAjYXJnaW5pbmUKICAgICAgICAgICAgICAgICIyNDEuMDMxMV84LjI4NCIgI2N5c3RpbmUKICAgICAgICAgICAgICAgICkKCiMgbWFrZSB0aWR5IGRmCnN0ZHNfZGZfZm9yc3RhdHNfdGlkeSA8LSBzdGRzX2RmX2Zvcl9zdGF0c193aWRlICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gMTI6bmNvbCguKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibXpfcnQiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAicmVsX2FidW5kX2xvZzIiKQpgYGAKCgpgYGB7cn0KIyBjaGFuZ2luZyBmYWN0b3IgbGV2ZWxzIGZvciBwcmVfcG9zdF9pbnRlcnZlbnRpb24Kc3Rkc19kZl9mb3JzdGF0c190aWR5JHByZV9wb3N0X2ludGVydmVudGlvbiA8LSBmYWN0b3Ioc3Rkc19kZl9mb3JzdGF0c190aWR5JHByZV9wb3N0X2ludGVydmVudGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygicHJlX1llbGxvdyIsICJwb3N0X1llbGxvdyIsICJwcmVfUmVkIiwgInBvc3RfUmVkIikpCgpsZXZlbHMoc3Rkc19kZl9mb3JzdGF0c190aWR5JHByZV9wb3N0X2ludGVydmVudGlvbikgIApgYGAKCgojIyMjIEJveHBsb3RzCmBgYHtyLCBmaWcud2lkdGg9MTJ9CnN0ZHNfZGZfZm9yc3RhdHNfdGlkeSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLCB5ID0gcmVsX2FidW5kX2xvZzIsIGZpbGwgPSBJbnRlcnZlbnRpb24pKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlllbGxvdyIgPSAiZ29sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVkIiA9ICJ0b21hdG8xIikpICsKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gU3ViamVjdCksIGNvbG91ciA9ICJwdXJwbGUiLCBsaW5ld2lkdGggPSAwLjIpICsKICBmYWNldF93cmFwKHZhcnMobXpfcnQpLCBzY2FsZXMgPSAiZnJlZV95IikgKyAKICB0aGVtZV9idygpIApgYGAKCiMjIyMgQ29udHJvbCB0LXRlc3QKYGBge3J9CiMgcnVuIHBhaXJlZCB0LXRlc3RzIGZvciBiZWZvcmUgdnMuIGFmdGV0IGNvbnRyb2wgaW50ZXJ2ZW50aW9uCnN0ZHNfY3RybF90LnRlc3RfcGFpcmVkIDwtIHN0ZHNfZGZfZm9yc3RhdHNfdGlkeSAlPiUKICBmaWx0ZXIoSW50ZXJ2ZW50aW9uID09ICJZZWxsb3ciKSAlPiUKICBkcGx5cjo6c2VsZWN0KFN1YmplY3QsIHByZV9wb3N0LCBtel9ydCwgcmVsX2FidW5kX2xvZzIpICU+JQogIGZpbHRlcihTdWJqZWN0ICE9IGMoNjEwNiwgNjExMikpICU+JSAjIHJlbW92ZSBvdXRsaWVycwogIGdyb3VwX2J5KG16X3J0KSAlPiUKICB0X3Rlc3QocmVsX2FidW5kX2xvZzIgfiBwcmVfcG9zdCwgCiAgICAgICAgIHBhaXJlZCA9IFRSVUUpICU+JSAKICBhZGRfc2lnbmlmaWNhbmNlKCkKYGBgCgpTdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGZlYXR1cmVzCmBgYHtyfQojIHdoaWNoIGZlYXR1cmVzIGFyZSBzaWduaWZpY2FudD8Kc3Rkc19jdHJsX3QudGVzdF9wYWlyZWRfc2lnIDwtIHN0ZHNfY3RybF90LnRlc3RfcGFpcmVkICU+JQogIGZpbHRlcihwIDw9IDAuMDUpCnRpYmJsZShzdGRzX2N0cmxfdC50ZXN0X3BhaXJlZF9zaWcpCgojIGhvdyBtYW55IGFyZSBzaWduaWZpY2FudD8KbnJvdyhzdGRzX2N0cmxfdC50ZXN0X3BhaXJlZF9zaWcpCmBgYAoKIyMjIyBSZWQgdC10ZXN0cwpgYGB7cn0KIyBydW4gcGFpcmVkIHQtdGVzdHMgZm9yIGJlZm9yZSB2cy4gYWZ0ZXIgcmVkIGludGVydmVudGlvbgpzdGRzX3JlZF90LnRlc3RfcGFpcmVkIDwtIHN0ZHNfZGZfZm9yc3RhdHNfdGlkeSAlPiUKICBmaWx0ZXIoSW50ZXJ2ZW50aW9uID09ICJSZWQiKSAlPiUKICBkcGx5cjo6c2VsZWN0KFN1YmplY3QsIHByZV9wb3N0LCBtel9ydCwgcmVsX2FidW5kX2xvZzIpICU+JQogIGZpbHRlcihTdWJqZWN0ICE9IGMoNjEwNiwgNjExMikpICU+JQogIGdyb3VwX2J5KG16X3J0KSAlPiUKICB0X3Rlc3QocmVsX2FidW5kX2xvZzIgfiBwcmVfcG9zdCwgCiAgICAgICAgIHBhaXJlZCA9IFRSVUUpICU+JSAKICBhZGRfc2lnbmlmaWNhbmNlKCkKYGBgCgpTdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGZlYXR1cmVzCmBgYHtyfQojIHdoaWNoIGZlYXR1cmVzIGFyZSBzaWduaWZpY2FudD8Kc3Rkc19yZWRfdC50ZXN0X3BhaXJlZF9zaWcgPC0gc3Rkc19yZWRfdC50ZXN0X3BhaXJlZCAlPiUKICBmaWx0ZXIocCA8PSAwLjA1KQp0aWJibGUoc3Rkc19yZWRfdC50ZXN0X3BhaXJlZF9zaWcpCgojIGhvdyBtYW55IGFyZSBzaWduaWZpY2FudD8KbnJvdyhzdGRzX3JlZF90LnRlc3RfcGFpcmVkX3NpZykKYGBgCgojIyMjIEludGVydmVudGlvbiB0LXRlc3RzCgpgYGB7cn0KIyBydW4gcGFpcmVkIHQtdGVzdHMgZm9yIHBvc3QtcmVkIHZzLiBwb3N0LWNvbnRyb2wgaW50ZXJ2ZW50aW9uCnN0ZHNfcG9zdF90LnRlc3RfcGFpcmVkIDwtIHN0ZHNfZGZfZm9yc3RhdHNfdGlkeSAlPiUKICBmaWx0ZXIocHJlX3Bvc3QgPT0gInBvc3QiKSAlPiUKICBkcGx5cjo6c2VsZWN0KFN1YmplY3QsIHByZV9wb3N0X2ludGVydmVudGlvbiwgbXpfcnQsIHJlbF9hYnVuZF9sb2cyKSAlPiUKICBmaWx0ZXIoU3ViamVjdCAhPSBjKDYxMDYsIDYxMTIpKSAlPiUKICBncm91cF9ieShtel9ydCkgJT4lCiAgdF90ZXN0KHJlbF9hYnVuZF9sb2cyIH4gcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLCAKICAgICAgICAgcGFpcmVkID0gVFJVRSkgJT4lIAogIGFkZF9zaWduaWZpY2FuY2UoKQpgYGAKClN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZmVhdHVyZXMKYGBge3J9CiMgd2hpY2ggZmVhdHVyZXMgYXJlIHNpZ25pZmljYW50PwpzdGRzX3Bvc3RfdC50ZXN0X3BhaXJlZF9zaWcgPC0gc3Rkc19wb3N0X3QudGVzdF9wYWlyZWQgJT4lCiAgZmlsdGVyKHAgPD0gMC4wNSkKdGliYmxlKHN0ZHNfcG9zdF90LnRlc3RfcGFpcmVkX3NpZykKCiMgaG93IG1hbnkgYXJlIHNpZ25pZmljYW50Pwpucm93KHN0ZHNfcG9zdF90LnRlc3RfcGFpcmVkX3NpZykKYGBgCgojIyMgVm9sY2FubyBwbG90cwoKIyMjIyBQb3N0LWludGVydmVudGlvbiBjb21wYXJpc29ucyAKCiMjIyMjIFdyYW5nbGUgKG5vIG91dGxpZXIpCmBgYHtyfQojIGFkZCByZWd1bGFyIHJlbGF0aXZlIGFidW5kYW5jZSBsZXZlbHMgYmFjayBpbnRvIHRoZSBkZgpkZl9mb3Jfc3RhdHMgPC0gZGZfZm9yX3N0YXRzICU+JQogIG11dGF0ZShyZWxfYWJ1bmQgPSAyXihyZWxfYWJ1bmRfbG9nMikpCgojIGNhbGN1bGF0ZSBtZWFuIHJlbCBhYnVuZCAobm90IGxvZykgYnkgc2FtcGxlLCBhbmQgYXZnIGZvbGQgY2hhbmdlIChGQykgZGlmZiBieSBmZWF0dXJlCnJlZF92X3llbF92b2xjYW5vX2RhdGFfbm9PdXRsaWVyIDwtIGRmX2Zvcl9zdGF0cyAlPiUKICBmaWx0ZXIocHJlX3Bvc3QgPT0gInBvc3QiLAogICAgICAgICBTdWJqZWN0ICE9IDYxMDYsCiAgICAgICAgIFN1YmplY3QgIT0gNjExMikgJT4lICMgcmVtb3ZlIG91dGxpZXIgc3ViagogIGdyb3VwX2J5KEludGVydmVudGlvbiwgbXpfcnQpICU+JQogIHN1bW1hcml6ZShtZWFuX3JlbF9hYnVuZCA9IG1lYW4ocmVsX2FidW5kKSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEludGVydmVudGlvbiwgdmFsdWVzX2Zyb20gPSBtZWFuX3JlbF9hYnVuZCkgJT4lCiAgbXV0YXRlKEZDX3Bvc3RSZWRfZGl2X3Bvc3RZZWxsb3cgPSBSZWQvWWVsbG93KSAKCiMgYmluZCBiYWNrIHB2YWwKcmVkX3ZfeWVsX3RvYmluZF9ub091dGxpZXIgPC0gcG9zdF9ub091dGxpZXJfdC50ZXN0X3BhaXJlZCAlPiUKICBkcGx5cjo6c2VsZWN0KHApCgojIGNhbGN1bGF0ZSBsb2cyRkMsIGFuZCAtbG9nMTBwCnJlZF92X3llbF92b2xjYW5vX2RhdGFfbm9PdXRsaWVyIDwtIAogIGJpbmRfY29scyhyZWRfdl95ZWxfdm9sY2Fub19kYXRhX25vT3V0bGllciwgcmVkX3ZfeWVsX3RvYmluZF9ub091dGxpZXIpICU+JQogIG11dGF0ZShsb2cyX0ZDX3Bvc3RSZWRfZGl2X3Bvc3RZZWxsb3cgPSBpZl9lbHNlKEZDX3Bvc3RSZWRfZGl2X3Bvc3RZZWxsb3cgPiAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZzIoRkNfcG9zdFJlZF9kaXZfcG9zdFllbGxvdyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLShsb2cyKGFicyhGQ19wb3N0UmVkX2Rpdl9wb3N0WWVsbG93KSkpKSwgCiAgICAgICAgIG5lZ2xvZzEwcCA9IC1sb2cxMChwKSkKCgojIGNyZWF0ZSBhIGRmIG9mIGZlYXR1cmVzIHBhc3NpbmcgRkMgYW5kIHB2YWwgY3V0b2ZmcyBoaWdoZXIgaW4gcG9zdC1SZWQKcG9zdFJlZF9zaWdfcmVkX3ZfeWVsX3ZvbGNhbm9fbm9PdXRsaWVyIDwtIHJlZF92X3llbF92b2xjYW5vX2RhdGFfbm9PdXRsaWVyICU+JQogIGZpbHRlcihwIDw9IDAuMDUgJiBsb2cyX0ZDX3Bvc3RSZWRfZGl2X3Bvc3RZZWxsb3cgPj0gMC42KQoKIyBjcmVhdGUgYSBkZiBvZiBmZWF0dXJlcyBwYXNzaW5nIEZDIGFuZCBwdmFsIGN1dG9mZnMgaGlnaGVyIGluIHBvc3QtY29udHJvbApwb3N0WWVsbG93X3NpZ19yZWRfdl95ZWxfdm9sY2Fub19ub091dGxpZXIgPC0gcmVkX3ZfeWVsX3ZvbGNhbm9fZGF0YV9ub091dGxpZXIgJT4lCiAgZmlsdGVyKHAgPD0gMC4wNSAmIGxvZzJfRkNfcG9zdFJlZF9kaXZfcG9zdFllbGxvdyA8PSAtMC42KSAgCmBgYAoKIyMjIyMgRXhwb3J0IHNpZyBmZWF0dXJlcwoKQ3JlYXRlIGZlYXR1cmUgbGlzdHMgd2l0aCBzaWduaWZpY2FudCBmZWF0dXJlcyBhbG9uZyB3aXRoIHRoZWlyIGNsdXN0ZXJzCmBgYHtyfQojcG9zdC1SZWQgbGlzdApwb3N0UmVkX3NpZ19pbnRydm50bl9jb21wX2NsdXN0ZXJzIDwtIGxlZnRfam9pbihwb3N0UmVkX3NpZ19yZWRfdl95ZWxfdm9sY2Fub19ub091dGxpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfZmVhdHVyZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gIm16X3J0IikKCgoKI3Bvc3QtWWVsbG93IGxpc3QKcG9zdFllbGxvd19zaWdfaW50cnZudG5fY29tcF9jbHVzdGVycyA8LSBsZWZ0X2pvaW4ocG9zdFllbGxvd19zaWdfcmVkX3ZfeWVsX3ZvbGNhbm9fbm9PdXRsaWVyLCBjbHVzdGVyX2ZlYXR1cmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gIm16X3J0IikKCgpgYGAKCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBleHBvcnQgbGlzdCA6IHNpZyBoaWdoZXIgcG9zdCByZWQgCndyaXRlX2Nzdihwb3N0UmVkX3NpZ19pbnRydm50bl9jb21wX2NsdXN0ZXJzLAogICAgICAgICAgIkZlYXR1cmUgbGlzdHMvcG9zdFJlZC1zaWdmZWF0dXJlcy1pbnRlcnZudG4tY29tcC5jc3YiKQoKIyBleHBvcnQgbGlzdCA6IHNpZyBoaWdoZXIgcG9zdCB5ZWxsb3cgCndyaXRlX2Nzdihwb3N0WWVsbG93X3NpZ19pbnRydm50bl9jb21wX2NsdXN0ZXJzLAogICAgICAgICAgIkZlYXR1cmUgbGlzdHMvcG9zdFllbGxvdy1zaWdmZWF0dXJlcy1pbnRlcnZudG4tY29tcC5jc3YiKQpgYGAKCgojIyMjIyBQbG90CmBgYHtyfQoocmVkX3ZfeWVsbG93X3ZvbGNhbm9fbm9PdXRsaWVyIDwtIHJlZF92X3llbF92b2xjYW5vX2RhdGFfbm9PdXRsaWVyICU+JQogIGdncGxvdChhZXMoeCA9IGxvZzJfRkNfcG9zdFJlZF9kaXZfcG9zdFllbGxvdywgeSA9IG5lZ2xvZzEwcCwgCiAgICAgICAgICAgICB0ZXh0ID0gZ2x1ZSgiTWFzc19yZXRlbnRpb24gdGltZToge216X3J0fQogICAgICAgICAgICAgICAgICAgICAgICAgUC12YWx1ZToge3B9CiAgICAgICAgICAgICAgICAgICAgICAgICBGb2xkIGNoYW5nZSB0b21hdG8vY29udHJvbDoge3JvdW5kKEZDX3Bvc3RSZWRfZGl2X3Bvc3RZZWxsb3csIDIpfSIpKSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiZ3JleSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBwb3N0UmVkX3NpZ19pbnRydm50bl9jb21wX2NsdXN0ZXJzLCAKICAgICAgICAgICAgIGFlcyh4ID0gbG9nMl9GQ19wb3N0UmVkX2Rpdl9wb3N0WWVsbG93LCB5ID0gbmVnbG9nMTBwKSwKICAgICAgICAgICAgIGNvbG9yID0gInRvbWF0byIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBwb3N0WWVsbG93X3NpZ19pbnRydm50bl9jb21wX2NsdXN0ZXJzLCAKICAgICAgICAgICAgIGFlcyh4ID0gbG9nMl9GQ19wb3N0UmVkX2Rpdl9wb3N0WWVsbG93LCB5ID0gbmVnbG9nMTBwKSwKICAgICAgICAgICAgIGNvbG9yID0gInllbGxvdzIiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMC42LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmV5IikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IC0wLjYsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXkiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMS4zLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmV5IikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtNSwgOCkpICsKICBsYWJzKHRpdGxlID0gIlZvbGNhbm8gUGxvdCBvZiBGZWF0dXJlcyBEaWZmZXJlbnQgaW4gUGVvcGxlIEFmdGVyIFRvbWF0by1Tb3kgYW5kIENvbnRyb2wgSnVpY2UgQ29uc3VtcHRpb24iLAogICAgICAgc3VidGl0bGUgPSAiUmVkIHBvaW50cyBhcmUgaGlnaGVyIGFmdGVyIHRvbWF0by1zb3kganVpY2UgY29uc3VtcHRpb24gd2hpbGUgeWVsbG93IHBvaW50cyBhcmUgaGlnaGVyIFxuYWZ0ZXIgY29udHJvbCB0b21hdG8ganVpY2UgY29uc3VtcHRpb24uIFN1YmplY3RzIDYxMDYgYW5kIDYxMTIgcmVtb3ZlZCIsCiAgICAgICBjYXB0aW9uID0gIlZlcnRpY2FsIGRhc2hlZCBsaW5lcyByZXByZXNlbnQgYSBsb2cyIGZvbGQgY2hhbmdlID4gMC42IG9yIDwgLTAuNiwgYW5kIGhvcml6b250YWwgZGFzaGVkIGxpbmUgcmVwcmVzZW50cyBhbiBGRFIgY29ycmVjdGVkIFxucC12YWx1ZSBvZiAwLjA1LiIsCiAgICAgICB4ID0gIkxvZzIgRm9sZCBDaGFuZ2UgKFRvbWF0b1NveS9Db250cm9sKSIsCiAgICAgICB5ID0gIi1Mb2cxMChQLXZhbHVlKSIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgaGp1c3QgPSAwKSwKICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSkKCihyZWRfdl95ZWxsb3dfdm9sY2Fub19nZ3Bsb3RseV9ub091dGxpZXIgPC0gZ2dwbG90bHkocmVkX3ZfeWVsbG93X3ZvbGNhbm9fbm9PdXRsaWVyLCB0b29sdGlwID0gInRleHQiKSkKYGBgCgpTYXZlIHBsb3RzCmBgYHtyLCBldmFsPUZBTFNFfQojIHNhdmUgdm9sY2FubyBwbG90Cmdnc2F2ZShwbG90ID0gcmVkX3ZfeWVsbG93X3ZvbGNhbm9fbm9PdXRsaWVyLAogICAgICAgZmlsZW5hbWUgPSAicGxvdHMgYW5kIGZpZ3VyZXMvdm9sY2FubyBwbG90cy9yZWRfdl95ZWxsb3dfdm9sY2Fub19ub091dGxpZXIuc3ZnIikKCiMgc2F2ZSBpbnRlcmFjdGl2ZSB2b2xjYW5vIHBsb3QKc2F2ZVdpZGdldCh3aWRnZXQgPSByZWRfdl95ZWxsb3dfdm9sY2Fub19nZ3Bsb3RseV9ub091dGxpZXIsCiAgICAgICAgICAgZmlsZSA9ICJwbG90cyBhbmQgZmlndXJlcy92b2xjYW5vIHBsb3RzL2ludGVyYWN0aXZlX3JlZHZ5ZWxsb3dfdm9sY2Fub19wbG90X25vT3V0bGllci5odG1sIikKYGBgCgoKIyMjIyBSZWQKCiMjIyMjIFdyYW5nbGUgKG5vIG91dGxpZXIpCmBgYHtyfQojIGNhbGN1bGF0ZSBtZWFuIHJlbCBhYnVuZCAobm90IGxvZykgYnkgc2FtcGxlLCBhbmQgYXZnIGZvbGQgY2hhbmdlIChGQykgZGlmZiBieSBmZWF0dXJlCnJlZF92b2xjYW5vX2RhdGFfbm9PdXRsaWVyIDwtIGRmX2Zvcl9zdGF0cyAlPiUKICBmaWx0ZXIoSW50ZXJ2ZW50aW9uID09ICJSZWQiLAogICAgICAgICBTdWJqZWN0ICE9IDYxMDYsCiAgICAgICAgIFN1YmplY3QgIT0gNjExMikgJT4lCiAgZ3JvdXBfYnkocHJlX3Bvc3QsIG16X3J0KSAlPiUKICBzdW1tYXJpemUobWVhbl9yZWxfYWJ1bmQgPSBtZWFuKHJlbF9hYnVuZCkpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBwcmVfcG9zdCwgdmFsdWVzX2Zyb20gPSBtZWFuX3JlbF9hYnVuZCkgJT4lCiAgbXV0YXRlKEZDX3Bvc3RfZGl2X3ByZSA9IHBvc3QvcHJlKSAKCiMgYmluZCBiYWNrIHB2YWwKcmVkX3RvYmluZF9ub091dGxpZXIgPC0gcmVkX25vT3V0bGllcl90LnRlc3RfcGFpcmVkICU+JQogIGRwbHlyOjpzZWxlY3QocCkKCiMgY2FsY3VsYXRlIGxvZzJGQywgYW5kIC1sb2cxMHAKcmVkX3ZvbGNhbm9fZGF0YV9ub091dGxpZXIgPC0gCiAgYmluZF9jb2xzKHJlZF92b2xjYW5vX2RhdGFfbm9PdXRsaWVyLCByZWRfdG9iaW5kX25vT3V0bGllcikgJT4lCiAgbXV0YXRlKGxvZzJfRkNfcG9zdF9kaXZfcHJlID0gbG9nMihGQ19wb3N0X2Rpdl9wcmUpLAogICAgICAgICBuZWdsb2cxMHAgPSAtbG9nMTAocCkpCgoKIyBjcmVhdGUgYSBkZiBvZiBmZWF0dXJlcyBwYXNzaW5nIEZDIGFuZCBwdmFsIGN1dG9mZnMgaGlnaGVyIGluIHBvc3QtaW50ZXJ2ZW50aW9uIGNvbXBhcmVkIHRvIHByZQpyZWRfcHJlX3ZfcG9zdF92b2xjYW5vX25vT3V0bGllciA8LSByZWRfdm9sY2Fub19kYXRhX25vT3V0bGllciAlPiUKICBmaWx0ZXIocCA8PSAwLjA1ICYgYWJzKGxvZzJfRkNfcG9zdF9kaXZfcHJlKSA+PSAwLjYpCgpgYGAKCiMjIyMjIEV4cG9ydCBzaWcgZmVhdHVyZXMKCkNyZWF0ZSBmZWF0dXJlIGxpc3RzIHdpdGggc2lnbmlmaWNhbnQgZmVhdHVyZXMgYWxvbmcgd2l0aCB0aGVpciBjbHVzdGVycwpgYGB7cn0KcmVkX3NpZ19wcmVwb3N0X2NvbXBfY2x1c3RlcnMgPC0gbGVmdF9qb2luKHJlZF9wcmVfdl9wb3N0X3ZvbGNhbm9fbm9PdXRsaWVyLCBjbHVzdGVyX2ZlYXR1cmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gIm16X3J0IikKCmBgYAoKCmBgYHtyLCBldmFsPUZBTFNFfQojIGV4cG9ydCAKd3JpdGVfY3N2KHJlZF9zaWdfcHJlcG9zdF9jb21wX2NsdXN0ZXJzLAogICAgICAgICAgIkZlYXR1cmUgbGlzdHMvUmVkLXNpZ2ZlYXR1cmVzLVByZXZzUG9zdC1ub091dGxpZXJzLmNzdiIpCmBgYAoKCiMjIyMjIFBsb3QKYGBge3J9CihyZWRfdm9sY2Fub19ub091dGxpZXIgPC0gcmVkX3ZvbGNhbm9fZGF0YV9ub091dGxpZXIgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9nMl9GQ19wb3N0X2Rpdl9wcmUsIHkgPSBuZWdsb2cxMHAsIAogICAgICAgICAgICAgdGV4dCA9IGdsdWUoIk1hc3NfcmV0ZW50aW9uIHRpbWU6IHttel9ydH0KICAgICAgICAgICAgICAgICAgICAgICAgIFAtdmFsdWU6IHtwfQogICAgICAgICAgICAgICAgICAgICAgICAgRm9sZCBjaGFuZ2UgcG9zdC9wcmU6IHtyb3VuZChGQ19wb3N0X2Rpdl9wcmUsIDIpfSIpKSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiZ3JleSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSByZWRfc2lnX3ByZXBvc3RfY29tcF9jbHVzdGVycywgCiAgICAgICAgICAgICBhZXMoeCA9IGxvZzJfRkNfcG9zdF9kaXZfcHJlLCB5ID0gbmVnbG9nMTBwKSwKICAgICAgICAgICAgIGNvbG9yID0gInRvbWF0byIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjYsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXkiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gLTAuNiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JleSIpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMS4zLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmV5IikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtNSwgNikpICsKICBsYWJzKHRpdGxlID0gIlZvbGNhbm8gUGxvdCBvZiBGZWF0dXJlcyBEaWZmZXJlbnQgaW4gUGVvcGxlIEFmdGVyIFRvbWF0by1Tb3kgSnVpY2UgQ29uc3VtcHRpb24iLAogICAgICAgc3VidGl0bGUgPSAiUmVkIHBvaW50cyBhcmUgaGlnaGVyIGFmdGVyIHRvbWF0by1zb3kganVpY2UgY29uc3VtcHRpb24gd2hlbiBjb21wYXJlZCB0byBwcmlvciB0byBjb25zdW1wdGlvbi4gXG5TdWJqZWN0cyA2MTA2IGFuZCA2MTEyIHJlbW92ZWQiLAogICAgICAgY2FwdGlvbiA9ICJWZXJ0aWNhbCBkYXNoZWQgbGluZXMgcmVwcmVzZW50IGFuIGFicyhsb2cgZm9sZCBjaGFuZ2UpID4gMC42LCBhbmQgaG9yaXpvbnRhbCBkYXNoZWQgbGluZSByZXByZXNlbnRzIGFuIEZEUiBjb3JyZWN0ZWQgXG5wLXZhbHVlIG9mIDAuMDUuIiwKICAgICAgIHggPSAiTG9nMiBGb2xkIENoYW5nZSAoUG9zdC9QcmUpIiwKICAgICAgIHkgPSAiLUxvZzEwKFAtdmFsdWUpIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBoanVzdCA9IDApLAogICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpKQoKKHJlZF92b2xjYW5vX2dncGxvdGx5X25vT3V0bGllciA8LSBnZ3Bsb3RseShyZWRfdm9sY2Fub19ub091dGxpZXIsIHRvb2x0aXAgPSAidGV4dCIpKQpgYGAKClNhdmUgcGxvdHMKYGBge3IsIGV2YWw9RkFMU0V9CiMgc2F2ZSB2b2xjYW5vIHBsb3QKZ2dzYXZlKHBsb3QgPSByZWRfdm9sY2Fub19ub091dGxpZXIsCiAgICAgICBmaWxlbmFtZSA9ICJwbG90cyBhbmQgZmlndXJlcy92b2xjYW5vIHBsb3RzL3JlZF92b2xjYW5vX25vT3V0bGllci5zdmciKQoKIyBzYXZlIGludGVyYWN0aXZlIHZvbGNhbm8gcGxvdApzYXZlV2lkZ2V0KHdpZGdldCA9IHJlZF92b2xjYW5vX2dncGxvdGx5X25vT3V0bGllciwKICAgICAgICAgICBmaWxlID0gInBsb3RzIGFuZCBmaWd1cmVzL3ZvbGNhbm8gcGxvdHMvaW50ZXJhY3RpdmVfcmVkX3ZvbGNhbm9fcGxvdF9ub091dGxpZXIuaHRtbCIpCmBgYAoKIyMjIyBZZWxsb3cKCiMjIyMjIFdyYW5nbGUgKG5vIG91dGxpZXIpCmBgYHtyfQojIGNhbGN1bGF0ZSBtZWFuIHJlbCBhYnVuZCAobm90IGxvZykgYnkgc2FtcGxlLCBhbmQgYXZnIGZvbGQgY2hhbmdlIChGQykgZGlmZiBieSBmZWF0dXJlCnllbF92b2xjYW5vX2RhdGFfbm9PdXRsaWVyIDwtIGRmX2Zvcl9zdGF0cyAlPiUKICBmaWx0ZXIoSW50ZXJ2ZW50aW9uID09ICJZZWxsb3ciLAogICAgICAgICBTdWJqZWN0ICE9IDYxMDYsCiAgICAgICAgIFN1YmplY3QgIT0gNjExMikgJT4lCiAgZ3JvdXBfYnkocHJlX3Bvc3QsIG16X3J0KSAlPiUKICBzdW1tYXJpemUobWVhbl9yZWxfYWJ1bmQgPSBtZWFuKHJlbF9hYnVuZCkpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBwcmVfcG9zdCwgdmFsdWVzX2Zyb20gPSBtZWFuX3JlbF9hYnVuZCkgJT4lCiAgbXV0YXRlKEZDX3Bvc3RfZGl2X3ByZSA9IHBvc3QvcHJlKSAKCiMgYmluZCBiYWNrIHB2YWwKeWVsX3RvYmluZF9ub091dGxpZXIgPC0gY3RybF9ub091dGxpZXJfdC50ZXN0X3BhaXJlZCAlPiUKICBkcGx5cjo6c2VsZWN0KHApCgojIGNhbGN1bGF0ZSBsb2cyRkMsIGFuZCAtbG9nMTBwCnllbF92b2xjYW5vX2RhdGFfbm9PdXRsaWVyIDwtIAogIGJpbmRfY29scyh5ZWxfdm9sY2Fub19kYXRhX25vT3V0bGllciwgeWVsX3RvYmluZF9ub091dGxpZXIpICU+JQogIG11dGF0ZShsb2cyX0ZDX3Bvc3RfZGl2X3ByZSA9IGxvZzIoRkNfcG9zdF9kaXZfcHJlKSwKICAgICAgICAgbmVnbG9nMTBwID0gLWxvZzEwKHApKQoKCiMgY3JlYXRlIGEgZGYgb2YgZmVhdHVyZXMgcGFzc2luZyBGQyBhbmQgcHZhbCBjdXRvZmZzIGhpZ2hlciBpbiBwb3N0LWludGVydmVudGlvbiBjb21wYXJlZCB0byBwcmUKeWVsX3ByZV92X3Bvc3Rfdm9sY2Fub19ub091dGxpZXIgPC0geWVsX3ZvbGNhbm9fZGF0YV9ub091dGxpZXIgJT4lCiAgZmlsdGVyKHAgPD0gMC4wNSAmIGFicyhsb2cyX0ZDX3Bvc3RfZGl2X3ByZSkgPj0gMC42KQoKYGBgCgojIyMjIyBFeHBvcnQgc2lnIGZlYXR1cmVzCgpDcmVhdGUgZmVhdHVyZSBsaXN0cyB3aXRoIHNpZ25pZmljYW50IGZlYXR1cmVzIGFsb25nIHdpdGggdGhlaXIgY2x1c3RlcnMKYGBge3J9CnllbF9zaWdfcHJlcG9zdF9jb21wX2NsdXN0ZXJzIDwtIGxlZnRfam9pbih5ZWxfcHJlX3ZfcG9zdF92b2xjYW5vX25vT3V0bGllciwgY2x1c3Rlcl9mZWF0dXJlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJtel9ydCIpCgpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQojIGV4cG9ydAp3cml0ZV9jc3YoeWVsX3NpZ19wcmVwb3N0X2NvbXBfY2x1c3RlcnMsCiAgICAgICAgICAiWWVsbG93LXNpZ2ZlYXR1cmVzLVByZXZzUG9zdC1ub091dGxpZXJzLmNzdiIpCmBgYAoKCiMjIyMjIFBsb3QKYGBge3J9Cih5ZWxfdm9sY2Fub19ub091dGxpZXIgPC0geWVsX3ZvbGNhbm9fZGF0YV9ub091dGxpZXIgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9nMl9GQ19wb3N0X2Rpdl9wcmUsIHkgPSBuZWdsb2cxMHAsIAogICAgICAgICAgICAgdGV4dCA9IGdsdWUoIk1hc3NfcmV0ZW50aW9uIHRpbWU6IHttel9ydH0KICAgICAgICAgICAgICAgICAgICAgICAgIFAtdmFsdWU6IHtwfQogICAgICAgICAgICAgICAgICAgICAgICAgRm9sZCBjaGFuZ2UgcG9zdC9wcmU6IHtyb3VuZChGQ19wb3N0X2Rpdl9wcmUsIDIpfSIpKSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiZ3JleSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSB5ZWxfc2lnX3ByZXBvc3RfY29tcF9jbHVzdGVycywgCiAgICAgICAgICAgICBhZXMoeCA9IGxvZzJfRkNfcG9zdF9kaXZfcHJlLCB5ID0gbmVnbG9nMTBwKSwKICAgICAgICAgICAgIGNvbG9yID0gInllbGxvdzIiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMC42LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmV5IikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IC0wLjYsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXkiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMS4zLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJncmV5IikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtNiwgNikpICsKICBsYWJzKHRpdGxlID0gIlZvbGNhbm8gUGxvdCBvZiBGZWF0dXJlcyBEaWZmZXJlbnQgaW4gUGVvcGxlIEFmdGVyIENvbnRyb2wsIFllbGxvdyBUb21hdG8gSnVpY2UgQ29uc3VtcHRpb24iLAogICAgICAgc3VidGl0bGUgPSAiWWVsbG93IHBvaW50cyBhcmUgaGlnaGVyIGFmdGVyIGNvbnRyb2wganVpY2UgY29uc3VtcHRpb24gd2hlbiBjb21wYXJlZCB0byBwcmlvciB0byBjb25zdW1wdGlvbi5cblN1YmplY3RzIDYxMDYgYW5kIDYxMTIgcmVtb3ZlZCIsCiAgICAgICBjYXB0aW9uID0gIlZlcnRpY2FsIGRhc2hlZCBsaW5lcyByZXByZXNlbnQgYWJzKGxvZzIgZm9sZCBjaGFuZ2UpID4gMC42LCBhbmQgaG9yaXpvbnRhbCBkYXNoZWQgbGluZSByZXByZXNlbnRzIGFuIEZEUiBjb3JyZWN0ZWQgXG5wLXZhbHVlIG9mIDAuMDUuIiwKICAgICAgIHggPSAiTG9nMiBGb2xkIENoYW5nZSAoUG9zdC9QcmUpIiwKICAgICAgIHkgPSAiLUxvZzEwKFAtdmFsdWUpIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBoanVzdCA9IDApLAogICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpKQoKKHllbF92b2xjYW5vX2dncGxvdGx5X25vT3V0bGllciA8LSBnZ3Bsb3RseSh5ZWxfdm9sY2Fub19ub091dGxpZXIsIHRvb2x0aXAgPSAidGV4dCIpKQpgYGAKClNhdmUgcGxvdHMKYGBge3IsIGV2YWw9RkFMU0V9CiMgc2F2ZSB2b2xjYW5vIHBsb3QKZ2dzYXZlKHBsb3QgPSB5ZWxfdm9sY2Fub19ub091dGxpZXIsCiAgICAgICBmaWxlbmFtZSA9ICJwbG90cyBhbmQgZmlndXJlcy92b2xjYW5vIHBsb3RzL3llbF92b2xjYW5vX25vT3V0bGllci5zdmciKQoKIyBzYXZlIGludGVyYWN0aXZlIHZvbGNhbm8gcGxvdApzYXZlV2lkZ2V0KHdpZGdldCA9IHllbF92b2xjYW5vX2dncGxvdGx5X25vT3V0bGllciwKICAgICAgICAgICBmaWxlID0gInBsb3RzIGFuZCBmaWd1cmVzL3ZvbGNhbm8gcGxvdHMvaW50ZXJhY3RpdmVfeWVsX3ZvbGNhbm9fcGxvdF9ub091dGxpZXIuaHRtbCIpCmBgYAoKCiMjIEpvaW5lZCBsaXN0cwpIZXJlLCBJIHdhbnQgdG8gdmVubiBzaWduaWZpY2FudCBmZWF0dXJlcyBiZWZvcmUgSSBiZWdpbiB0byBsb29rIG1vcmUgaW50byB0aGVtLiBJIGFtIGludGVyZXN0ZWQgaW4gdGhlIGZvbGxvd2luZyBlZmZlY3RzOiB0b21hdG8gZWZmZWN0LCBseWNvcGVuZSBhbmQvb3Igc295IGlzb2ZsYXZvbmVzIGVmZmVjdC4KCiMjIyBUb21hdG8gZWZmZWN0CjEuIFRvbWF0byBlZmZlY3Q6IGpvaW4gYSBsaXN0IHRoYXQgb25seSBrZWVwcyBmZWF0dXJlcyB0aGF0IGFyZSBib3RoIHNpZ25pZmljYW50IGluIHByZSB2cy4gcG9zdC1yZWQgYW5kIHByZSB2cy4gcG9zdC15ZWxsb3cuCgpgYGB7cn0KIyBrZWVwIG9ubHkgZmVhdHVyZXMgcHJlc2VudCBpbiBib3RoIHByZSB2cyBwb3N0IHJlZCBhbmQgcHJlIHZzIHBvc3QgeWVsbG93CnRvbWF0b19lZmZlY3QgPC0gaW5uZXJfam9pbihyZWRfc2lnX3ByZXBvc3RfY29tcF9jbHVzdGVycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllbF9zaWdfcHJlcG9zdF9jb21wX2NsdXN0ZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAibXpfcnQiKQpkaW0odG9tYXRvX2VmZmVjdCkKYGBgCgpFeHBvcnQgdmVubmVkIGxpc3QKYGBge3IsIGV2YWw9RkFMU0V9CndyaXRlX2Nzdih0b21hdG9fZWZmZWN0LAogICAgICAgICAgIkZlYXR1cmUgbGlzdHMvc2lnLWZlYXR1cmVzLXRvbWF0by1lZmZlY3QuY3N2IikKYGBgCgoKIyMjIyBNdW1taWNob2cgbWF0Y2hlcwoKYGBge3J9CiMgcmVhZCBpbiBoaXRzIGZyb20gcmVkIApyZWRfbXVtbWljaG9nX2hpdHMgPC0gcmVhZF9jc3YoIi4uL211bW1pY2hvZyBhbmFseXNpcy9yZWQtaGlsaWMtbWl4ZWQgbW9kZSByZXN1bHRzLWludGVnL3JlZC1oaWxpYy1tdW1taWNob2dfbWF0Y2hlZF9jb21wb3VuZF9hbGwuY3N2IikgJT4lCiAgbXV0YXRlKFF1ZXJ5Lk1hc3MgPSByb3VuZChRdWVyeS5NYXNzLCBkaWdpdHMgPSA0KSkgJT4lICMga2VlcCA0IGRlY2ltYWwgcG9pbnRzIGZvciBtYXNzCiAgdW5pdGUobXpfcnQsCiAgICAgICAgYygiUXVlcnkuTWFzcyIsICJSZXRlbnRpb24uVGltZSIpLAogICAgICAgIHNlcCA9ICJfIikKCmBgYAoKTXVtbWljaG9nIG1ha2VzIGEgbGlzdCBvZiBoaXRzIGZvciBhbGwgb2YgdGhlIGNvbXBvdW5kcywgc28gd2Ugb25seSBuZWVkIHRvIGRvIGFuIGlubmVyIGpvaW4gZm9yIG9uZSBvZiB0aGUgbGlzdHMuIFRoZSBvdXRjb21lIHdvdWxkIGJlIHRoZSBzYW1lIGZvciBhbGwgb2YgdGhlIGxpc3RzIHVzZWQgc2luY2UgSSBoYXZlIHRvIGlucHV0IG15IHdob2xlIGRhdGFzZXQgKG5vIGN1dG9mZnMpIGludG8gYW5hbHlzaXMuIAoKYGBge3J9Cm11bW1pY2hvZ19tYXNzX21hdGNoZXNfdG9tYXRvIDwtIGlubmVyX2pvaW4odG9tYXRvX2VmZmVjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWRfbXVtbWljaG9nX2hpdHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAibXpfcnQiKQoKbGVuZ3RoKHVuaXF1ZShtdW1taWNob2dfbWFzc19tYXRjaGVzX3RvbWF0byRtel9ydCkpICMgaG93IG1hbnk/Cgp0aWJibGUodW5pcXVlKG11bW1pY2hvZ19tYXNzX21hdGNoZXNfdG9tYXRvJG16X3J0KSkgIyB3aGljaCBmZWF0dXJlcyBoYXZlIGEgaGl0IHdpdGggbXVtbWljaG9nPwoKYGBgCgoKYGBge3J9CiMgbWFwIEtFR0cgSURzIHRvIGNvbXBvdW5kIG5hbWVzCnRvbWF0b19LRUdHbWF0Y2hlcyA8LSBhcy5kYXRhLmZyYW1lKGNwZGtlZ2cybmFtZShtdW1taWNob2dfbWFzc19tYXRjaGVzX3RvbWF0byRNYXRjaGVkLkNvbXBvdW5kKSkKCm11bW1pY2hvZ19tYXNzX21hdGNoZXNfdG9tYXRvJE5BTUUgPC0gdG9tYXRvX0tFR0dtYXRjaGVzJE5BTUUKCmBgYAoKIyMjIyBCb3hwbG90cwoKCiMjIyMjIGFsbCBzaWcgdG9tYXRvIGZlYXR1cmVzCgpgYGB7cn0KIyBtZXRhYnMgd2l0aCBwdmFsIDwgMC4wNSBhbmQgZmMgPj0gMS41MQpzaWdtZXRhYnNfdG9tYXRvX2VmZmVjdCA8LSBhcy5jaGFyYWN0ZXIodG9tYXRvX2VmZmVjdCRtel9ydCkKYGBgCgpgYGB7cn0KdG9tYXRvX2VmZmVjdF9kZl9mb3Jfc3RhdHNfd2lkZSA8LSBkZl9mb3Jfc3RhdHMgJT4lCiAgZHBseXI6OnNlbGVjdCghcmVsX2FidW5kKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbXpfcnQsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSByZWxfYWJ1bmRfbG9nMikgJT4lCiAgZHBseXI6OnNlbGVjdChjKDE6MTEpLAogICAgICAgICAgICAgICAgc2lnbWV0YWJzX3RvbWF0b19lZmZlY3QpCgojIG1ha2UgdGlkeSBkZgp0b21hdG9fZWZmZWN0X2RmX2Zvcl9zdGF0c190aWR5IDwtIHRvbWF0b19lZmZlY3RfZGZfZm9yX3N0YXRzX3dpZGUgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAxMjpuY29sKC4pLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJtel9ydCIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJyZWxfYWJ1bmRfbG9nMiIpCmBgYAoKYGBge3J9CiMgY2hhbmdpbmcgZmFjdG9yIGxldmVscyBmb3IgcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uCnRvbWF0b19lZmZlY3RfZGZfZm9yX3N0YXRzX3RpZHkkcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uIDwtIGZhY3Rvcih0b21hdG9fZWZmZWN0X2RmX2Zvcl9zdGF0c190aWR5JHByZV9wb3N0X2ludGVydmVudGlvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygicHJlX1llbGxvdyIsICJwb3N0X1llbGxvdyIsICJwcmVfUmVkIiwgInBvc3RfUmVkIikpCgpsZXZlbHModG9tYXRvX2VmZmVjdF9kZl9mb3Jfc3RhdHNfdGlkeSRwcmVfcG9zdF9pbnRlcnZlbnRpb24pICAKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTE1fQp0b21hdG9fZWZmZWN0X2RmX2Zvcl9zdGF0c190aWR5ICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBwcmVfcG9zdF9pbnRlcnZlbnRpb24sIHkgPSByZWxfYWJ1bmRfbG9nMiwgZmlsbCA9IEludGVydmVudGlvbikpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiWWVsbG93IiA9ICJnb2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZWQiID0gInRvbWF0bzEiKSkgKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBTdWJqZWN0KSwgZmlsbCA9ICJicm93biIsIGxpbmV3aWR0aCA9IDAuMikgKwogIGZhY2V0X3dyYXAodmFycyhtel9ydCkpICsgCiAgdGhlbWVfYncoKSAKYGBgCgoKIyMjIEx5Yy9zb3kgZWZmZWN0CjIuIGx5Y29wZW5lIGFuZC9vciBzb3kgaXNvZmxhdm9uZXMgZWZmZWN0OiBqb2luIGEgbGlzdCB0aGF0IG9ubHkga2VlcHMgZmVhdHVyZXMgdGhhdCBhcmU6Ci0gc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgYmV0d2VlbiBwb3N0LVJlZCBhbmQgcG9zdC1ZZWxsb3csIAotIGFuZCBzaWduaWZpY2FudCBiZXR3ZWVuIHByZS0gYW5kIHBvc3QtUmVkLiAKLSByZW1vdmUgZmVhdHVyZXMgdGhhdCBhcmUgc2lnbmlmaWNhbnQgYmV0d2VlbiBwcmUtWWVsbG93IGFuZCBwb3N0LVllbGxvdy4KCmBgYHtyfQojIGNvbWJpbmUgc2lnIGZlYXR1cmVzIGZyb20gcG9zdC1yZWQgdnMgcG9zdC15ZWxsb3cKc2lnX3Bvc3RpbnRlcnZlbnRpb25fbm9PdXRsaWVyIDwtIGZ1bGxfam9pbihwb3N0UmVkX3NpZ19pbnRydm50bl9jb21wX2NsdXN0ZXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvc3RZZWxsb3dfc2lnX2ludHJ2bnRuX2NvbXBfY2x1c3RlcnMpCmRpbShzaWdfcG9zdGludGVydmVudGlvbl9ub091dGxpZXIpCgojIHNlbGVjdCBvbmx5IHNpZyBmZWF0dXJlcyB0aGF0IGFyZSBwcmVzZW50IHdoZW4gY29tcGFyaW5nIHByZS1SZWQgdG8gcG9zdC1SZWQKbHljX3NveV9lZmZlY3QgPC0gaW5uZXJfam9pbihzaWdfcG9zdGludGVydmVudGlvbl9ub091dGxpZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVkX3NpZ19wcmVwb3N0X2NvbXBfY2x1c3RlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAibXpfcnQiKSAKZGltKGx5Y19zb3lfZWZmZWN0KQojIHJlbW92ZSBmZWF0dXJlcyB0aGF0IGFyZSBzaWduaWZpY2FudCBwcmUtWWVsbG93IHZzIHBvc3QtWWVsbG93IGNvbXBhcmlzb24KIyBseWNfc295X2VmZmVjdCA8LSBhbnRpX2pvaW4obHljX3NveV9lZmZlY3QsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllbF9zaWdfcHJlcG9zdF9jb21wX2NsdXN0ZXJzLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJtel9ydCIpCiMgZGltKGx5Y19zb3lfZWZmZWN0KSAjIGhvdyBtYW55IGZlYXR1cmVzIHdlcmUgcmVtb3ZlZD8KYGBgCgpFeHBvcnQgdmVubmVkIGxpc3QKYGBge3IsIGV2YWw9RkFMU0V9CndyaXRlX2NzdihseWNfc295X2VmZmVjdCwKICAgICAgICAgICJGZWF0dXJlIGxpc3RzL3NpZy1mZWF0dXJlcy1seWMtc295LWVmZmVjdC5jc3YiKQpgYGAKCgojIyMjIE11bW1pY2hvZyBtYXRjaGVzCgpNdW1taWNob2cgbWFrZXMgYSBsaXN0IG9mIGhpdHMgZm9yIGFsbCBvZiB0aGUgY29tcG91bmRzLCBzbyB3ZSBvbmx5IG5lZWQgdG8gZG8gYW4gaW5uZXIgam9pbiBmb3Igb25lIG9mIHRoZSBsaXN0cy4gVGhlIG91dGNvbWUgd291bGQgYmUgdGhlIHNhbWUgZm9yIGFsbCBvZiB0aGUgbGlzdHMgdXNlZCBzaW5jZSBJIGhhdmUgdG8gaW5wdXQgbXkgd2hvbGUgZGF0YXNldCAobm8gY3V0b2ZmcykgaW50byBhbmFseXNpcy4gCgpgYGB7cn0KbXVtbWljaG9nX21hc3NfbWF0Y2hlc19seWNzb3kgPC0gaW5uZXJfam9pbihseWNfc295X2VmZmVjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWRfbXVtbWljaG9nX2hpdHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAibXpfcnQiKQoKbGVuZ3RoKHVuaXF1ZShtdW1taWNob2dfbWFzc19tYXRjaGVzX2x5Y3NveSRtel9ydCkpICMgaG93IG1hbnk/Cgp0aWJibGUodW5pcXVlKG11bW1pY2hvZ19tYXNzX21hdGNoZXNfbHljc295JG16X3J0KSkgIyB3aGljaCBmZWF0dXJlcyBoYXZlIGEgaGl0IHdpdGggbXVtbWljaG9nPwoKYGBgCgoKCgpgYGB7cn0KIyBtYXAgS0VHRyBJRHMgdG8gY29tcG91bmQgbmFtZXMKbHljc295X0tFR0dtYXRjaGVzIDwtIGFzLmRhdGEuZnJhbWUoY3Bka2VnZzJuYW1lKG11bW1pY2hvZ19tYXNzX21hdGNoZXNfbHljc295JE1hdGNoZWQuQ29tcG91bmQpKQoKbXVtbWljaG9nX21hc3NfbWF0Y2hlc19seWNzb3kkTkFNRSA8LSBseWNzb3lfS0VHR21hdGNoZXMkTkFNRQoKYGBgCgoKCiMjIyMgQm94cGxvdHMKCgojIyMjIyBhbGwgc2lnIHRvbWF0byBmZWF0dXJlcwoKYGBge3J9CiMgbWV0YWJzIHdpdGggcHZhbCA8IDAuMDUgYW5kIGZjID49IDEuNTEKc2lnbWV0YWJzX2x5Y3NveV9lZmZlY3QgPC0gbHljX3NveV9lZmZlY3QkbXpfcnQgCmBgYAoKYGBge3J9Cmx5Y3NveV9lZmZlY3RfZGZfZm9yX3N0YXRzX3dpZGUgPC0gZGZfZm9yX3N0YXRzICU+JQogIGRwbHlyOjpzZWxlY3QoIXJlbF9hYnVuZCkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG16X3J0LAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gcmVsX2FidW5kX2xvZzIpICU+JQogIGRwbHlyOjpzZWxlY3QoYygxOjExKSwKICAgICAgICAgICAgICAgIHNpZ21ldGFic19seWNzb3lfZWZmZWN0KQoKIyBtYWtlIHRpZHkgZGYKbHljc295X2VmZmVjdF9kZl9mb3Jfc3RhdHNfdGlkeSA8LSBseWNzb3lfZWZmZWN0X2RmX2Zvcl9zdGF0c193aWRlICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gMTI6bmNvbCguKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibXpfcnQiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAicmVsX2FidW5kX2xvZzIiKQpgYGAKCmBgYHtyfQojIGNoYW5naW5nIGZhY3RvciBsZXZlbHMgZm9yIHByZV9wb3N0X2ludGVydmVudGlvbgpseWNzb3lfZWZmZWN0X2RmX2Zvcl9zdGF0c190aWR5JHByZV9wb3N0X2ludGVydmVudGlvbiA8LSBmYWN0b3IobHljc295X2VmZmVjdF9kZl9mb3Jfc3RhdHNfdGlkeSRwcmVfcG9zdF9pbnRlcnZlbnRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoInByZV9ZZWxsb3ciLCAicG9zdF9ZZWxsb3ciLCAicHJlX1JlZCIsICJwb3N0X1JlZCIpKQoKbGV2ZWxzKGx5Y3NveV9lZmZlY3RfZGZfZm9yX3N0YXRzX3RpZHkkcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uKSAgCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9NDAsIGZpZy53aWR0aD0zMH0KbHljc295X2VmZmVjdF9kZl9mb3Jfc3RhdHNfdGlkeSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLCB5ID0gcmVsX2FidW5kX2xvZzIsIGZpbGwgPSBJbnRlcnZlbnRpb24pKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlllbGxvdyIgPSAiZ29sZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVkIiA9ICJ0b21hdG8xIikpICsKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gU3ViamVjdCksIGZpbGwgPSAiYnJvd24iLCBsaW5ld2lkdGggPSAwLjIpICsKICBmYWNldF93cmFwKHZhcnMobXpfcnQpLCBzY2FsZXMgPSAiZnJlZV95IikgKyAKICB0aGVtZV9jbGVhbigpIApgYGAKCgojIyMgTG93IGNhcm90ZW5vaWQgdG9tYXRvIGVmZmVjdAozLiB5ZWxsb3cgdG9tYXRvIGVmZmVjdDogdXNlIGxpc3QgdGhhdCBvbmx5IGtlZXBzIGZlYXR1cmVzIHRoYXQgYXJlIGJvdGggc2lnbmlmaWNhbnQgYmV0d2VlbiBwcmUgYW5kIHBvc3QteWVsbG93LCBhbmQgYWxzbyBzaWduaWZpY2FudCBiZXR3ZWVuIHBvc3QtUmVkIGFuZCBwb3N0LVllbGxvdwoKYGBge3J9CiMgc2lnIGZlYXR1cmVzIGZyb20gcG9zdC1yZWQgdnMgcG9zdC15ZWxsb3cKZGltKHNpZ19wb3N0aW50ZXJ2ZW50aW9uX25vT3V0bGllcikKCiMgc2VsZWN0IG9ubHkgc2lnIGZlYXR1cmVzIHRoYXQgYXJlIHByZXNlbnQgd2hlbiBjb21wYXJpbmcgcHJlLVllbGxvdyB0byBwb3N0LVllbGxvdwpsb3dfY2Fyb3RfdG9tYXRvX2VmZmVjdCA8LSBpbm5lcl9qb2luKHNpZ19wb3N0aW50ZXJ2ZW50aW9uX25vT3V0bGllciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWxfc2lnX3ByZXBvc3RfY29tcF9jbHVzdGVycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJtel9ydCIpIApkaW0obG93X2Nhcm90X3RvbWF0b19lZmZlY3QpCmBgYAoKRXhwb3J0IHZlbm5lZCBsaXN0CmBgYHtyLCBldmFsPUZBTFNFfQp3cml0ZV9jc3YobG93X2Nhcm90X3RvbWF0b19lZmZlY3QsCiAgICAgICAgICAiRmVhdHVyZSBsaXN0cy9zaWctZmVhdHVyZXMtbG93LWNhcm90LXRvbWF0by1lZmZlY3QuY3N2IikKYGBgCgojIyMjIE11bW1pY2hvZyBtYXNzZXMKYGBge3J9Cm11bW1pY2hvZ19tYXNzX21hdGNoZXNfeWVsbG93X3RvbWF0byA8LSBpbm5lcl9qb2luKGxvd19jYXJvdF90b21hdG9fZWZmZWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZF9tdW1taWNob2dfaGl0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9ICJtel9ydCIpCgp0aWJibGUodW5pcXVlKG11bW1pY2hvZ19tYXNzX21hdGNoZXNfeWVsbG93X3RvbWF0byRtel9ydCkpICMgd2hpY2ggZmVhdHVyZXMgaGF2ZSBhIGhpdCB3aXRoIG11bW1pY2hvZz8KbGVuZ3RoKHVuaXF1ZShtdW1taWNob2dfbWFzc19tYXRjaGVzX3llbGxvd190b21hdG8kbXpfcnQpKSAjIGhvdyBtYW55PwpgYGAKCmBgYHtyfQojIG1hcCBLRUdHIElEcyB0byBjb21wb3VuZCBuYW1lcwp5ZWxsb3dfS0VHR21hdGNoZXMgPC0gYXMuZGF0YS5mcmFtZShjcGRrZWdnMm5hbWUobXVtbWljaG9nX21hc3NfbWF0Y2hlc195ZWxsb3dfdG9tYXRvJE1hdGNoZWQuQ29tcG91bmQpKQoKbXVtbWljaG9nX21hc3NfbWF0Y2hlc195ZWxsb3dfdG9tYXRvJE5BTUUgPC0geWVsbG93X0tFR0dtYXRjaGVzJE5BTUUKCmBgYAoKIyMjIyBCb3hwbG90cwoKCiMjIyMjIGFsbCBzaWcgeWVsbG93IHRvbSBmZWF0dXJlcwoKYGBge3J9CiMgbWV0YWJzIHdpdGggcHZhbCA8IDAuMDUgYW5kIGZjID49IDEuNTEKc2lnbWV0YWJzX3llbGxvd19lZmZlY3QgPC0gYXMuY2hhcmFjdGVyKGxvd19jYXJvdF90b21hdG9fZWZmZWN0JG16X3J0KQpgYGAKCmBgYHtyfQp5ZWxsb3dfZWZmZWN0X2RmX2Zvcl9zdGF0c193aWRlIDwtIGRmX2Zvcl9zdGF0cyAlPiUKICBkcGx5cjo6c2VsZWN0KCFyZWxfYWJ1bmQpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBtel9ydCwKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHJlbF9hYnVuZF9sb2cyKSAlPiUKICBkcGx5cjo6c2VsZWN0KGMoMToxMSksCiAgICAgICAgICAgICAgICBzaWdtZXRhYnNfeWVsbG93X2VmZmVjdCkKCiMgbWFrZSB0aWR5IGRmCnllbGxvd19lZmZlY3RfZGZfZm9yX3N0YXRzX3RpZHkgPC0geWVsbG93X2VmZmVjdF9kZl9mb3Jfc3RhdHNfd2lkZSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IDEyOm5jb2woLiksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm16X3J0IiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInJlbF9hYnVuZF9sb2cyIikKYGBgCgpgYGB7cn0KIyBjaGFuZ2luZyBmYWN0b3IgbGV2ZWxzIGZvciBwcmVfcG9zdF9pbnRlcnZlbnRpb24KeWVsbG93X2VmZmVjdF9kZl9mb3Jfc3RhdHNfdGlkeSRwcmVfcG9zdF9pbnRlcnZlbnRpb24gPC0gZmFjdG9yKHllbGxvd19lZmZlY3RfZGZfZm9yX3N0YXRzX3RpZHkkcHJlX3Bvc3RfaW50ZXJ2ZW50aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJwcmVfWWVsbG93IiwgInBvc3RfWWVsbG93IiwgInByZV9SZWQiLCAicG9zdF9SZWQiKSkKCmxldmVscyh5ZWxsb3dfZWZmZWN0X2RmX2Zvcl9zdGF0c190aWR5JHByZV9wb3N0X2ludGVydmVudGlvbikgIApgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTJ9CnllbGxvd19lZmZlY3RfZGZfZm9yX3N0YXRzX3RpZHkgJT4lIAogIGdncGxvdChhZXMoeCA9IHByZV9wb3N0X2ludGVydmVudGlvbiwgeSA9IHJlbF9hYnVuZF9sb2cyLCBmaWxsID0gSW50ZXJ2ZW50aW9uKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJZZWxsb3ciID0gImdvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJlZCIgPSAidG9tYXRvMSIpKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IFN1YmplY3QpLCBmaWxsID0gImJyb3duIiwgbGluZXdpZHRoID0gMC4yKSArCiAgZmFjZXRfd3JhcCh2YXJzKG16X3J0KSwgc2NhbGVzID0gImZyZWVfeSIpICsgCiAgdGhlbWVfY2xlYW4oKSAKYGBgCgojIEltbXVubyBDb3JyZWxhdGlvbnMKCklMLTUsIElMLTEycDcwLCBhbmQgR00tQ1NGIHdlcmUgc2lnbmlmaWNhbnRseSBkZWNyZWFzZWQgYWZ0ZXIgdG9tYXRvLXNveSBpbnRlcnZlbnRpb24gKG9ubHkgd2hlbiBjb21wYXJpbmcgcHJlIHRvIHBvc3QgaW4gUmVkKSBhY2NvcmRpbmcgdG8gV2lsY294b24gcmFuay1zdW0gdGVzdHMgKHA8MC4wNSkuIEltbXVuZSBjZWxsIHR5cGVzIHdlcmUgc2lnbmlmaWNhbnRseSBhbHRlcmVkIGluIGFsbCAzIGNvbXBhcmlzb25zIC0gc28gbGV0J3Mgc2VlIGhvdyB0aGVzZSBzaWduaWZpY2FudCBpbW11bm8gb3V0Y29tZXMgY29ycmVsYXRlIHdpdGggbWV0YWJvbGl0ZXMgZm91bmQgdG8gYmUgc2lnbmlmaWNhbnQgYXQgcCA+IDAuMDUgYW5kIEZDID49MS42MS4KCkltcG9ydCBvdGhlciBvdXRjb21lcyAoY2Fyb3Rlbm9pZHMgYW5kIGltbXVub2xvZ3kgZGF0YSkKYGBge3J9CiMgbG9hZCBkYXRhCmNhcm90X2ltbXVub2xvZ3lfbWV0YSA8LSByZWFkX2V4Y2VsKCJDb21waWxlZERhdGFfUmVzdWx0c19NZXRhLnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAibWV0YWRhdGFfY29ycmVjdGVkX3dpdGhzZXF1ZW5jZSIpCgojIGNsZWFuIHVwIHZhcmlhYmxlIG5hbWVzIApjYXJvdF9pbW11bm9sb2d5X21ldGEgPC0gY2xlYW5fbmFtZXMoY2Fyb3RfaW1tdW5vbG9neV9tZXRhKQpgYGAKCiMjIFdyYW5nbGUgCgpgYGB7cn0KIyBjb252ZXJ0IHZhcmlhYmxlcyB0aGF0IHNob3VsZCBiZSBmYWN0b3JzIHRvIGZhY3RvcnMKY2Fyb3RfaW1tdW5vbG9neV9tZXRhIDwtIGNhcm90X2ltbXVub2xvZ3lfbWV0YSAlPiUKICBmaWx0ZXIoaW50ZXJ2ZW50aW9uICE9ICJCYXNlbGluZSIpICU+JQogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBjKCJwYXRpZW50X2lkIiwgInBlcmlvZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICJpbnRlcnZlbnRpb24iLCAiaW50ZXJ2ZW50aW9uX3dlZWsiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAicHJlX3Bvc3QiLCAic2V4IiwgInNlcXVlbmNlIiksCiAgICAgICAgICAgICAgICAuZm5zID0gYXMuZmFjdG9yKSkKCgojIHNvbWUgc3R1ZmYgY2FtZSBpbiBhcyBjaGFyYWN0ZXJzIGJ1dCBzaG91bGQgYmUgbnVtZXJpYwpjYXJvdF9pbW11bm9sb2d5X21ldGEgPC0gY2Fyb3RfaW1tdW5vbG9neV9tZXRhICU+JQogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBjKCJpbF8yIiwgImlsXzEwIiwgImlsXzEzIiwgImlsXzQiKSwKICAgICAgICAgICAgICAgIC5mbnMgPSBhcy5udW1lcmljKSkKCgoKIyBjaGFuZ2luZyBmYWN0b3IgbGV2ZWxzIGZvciBwcmVfcG9zdApjYXJvdF9pbW11bm9sb2d5X21ldGEkcHJlX3Bvc3QgPC0gZmFjdG9yKGNhcm90X2ltbXVub2xvZ3lfbWV0YSRwcmVfcG9zdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygicHJlIiwgInBvc3QiKSkKCmxldmVscyhjYXJvdF9pbW11bm9sb2d5X21ldGEkcHJlX3Bvc3QpICAgICAgICAKCiMgQ2FsY3VsYXRlIHRvdGFsX2Npc19seWMsIHRvdGFsX2x5YywgYW5kIHRvdGFsX2Nhcm90ZW5vaWRzCmNhcm90X2ltbXVub2xvZ3lfbWV0YSA8LSBjYXJvdF9pbW11bm9sb2d5X21ldGEgJT4lCiAgbXV0YXRlKHRvdGFsX2Npc19seWMgPSBvdGhlcl9jaXNfbHljICsgeDVfY2lzX2x5YywKICAgICAgICAgdG90YWxfbHljID0gYWxsX3RyYW5zX2x5YyArIHRvdGFsX2Npc19seWMsCiAgICAgICAgIHRvdGFsX2Nhcm90ZW5vaWRzID0gbHV0ZWluICsgemVheGFudGhpbiArIGJfY3J5cHRveGFudGhpbiArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFfY2Fyb3RlbmUgKyBiX2Nhcm90ZW5lICsgdG90YWxfbHljKQpgYGAKCgojIyMgUm0gb3V0bGllcnMgKyBtb3JlIHdyYW5nbGluZwoKYGBge3J9CiMgZ28gYmFjayB0byB3aWRlIGZvciBzdGF0cyBkZgpkZl9mb3Jfc3RhdHNfd2lkZV9ub091dGxpZXIgPC0gZGZfZm9yX3N0YXRzX25vT3V0bGllciAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbXpfcnQsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSByZWxfYWJ1bmRfbG9nMikKCiMgdGFrZSBvdXRsaWVycyBvdXQgb2YgY2Fyb3RfaW1tdW5vIGRmIApjYXJvdF9pbW11bm9sb2d5X21ldGFfbm9PdXRsaWVyIDwtIGNhcm90X2ltbXVub2xvZ3lfbWV0YSAlPiUKICBmaWx0ZXIocGF0aWVudF9pZCAhPSA2MTA2LAogICAgICAgICBwYXRpZW50X2lkICE9IDYxMTIpCgojIGFkZCBzaWcgaW1tdW5vIG91dGNvbWUgY29sdW1ucyB0byBkYXRhc2V0CmZvcmNvcnJfbWV0YWJzbG9nMl9pbW11bm9fbm9PdXRsaWVyIDwtIGRmX2Zvcl9zdGF0c193aWRlX25vT3V0bGllciAlPiUKICBtdXRhdGUoaWxfNSA9IGNhcm90X2ltbXVub2xvZ3lfbWV0YV9ub091dGxpZXIkaWxfNSwKICAgICAgICAgaWxfMTJwNzAgPSBjYXJvdF9pbW11bm9sb2d5X21ldGFfbm9PdXRsaWVyJGlsXzEycDcwLAogICAgICAgICBnbV9jc2YgPSBjYXJvdF9pbW11bm9sb2d5X21ldGFfbm9PdXRsaWVyJGdtX2NzZiwKICAgICAgICAgdG90YWxfbHljID0gY2Fyb3RfaW1tdW5vbG9neV9tZXRhX25vT3V0bGllciR0b3RhbF9seWMsCiAgICAgICAgICMgc2lnIGltbXVuZSBjZWxscyBwcmUgdnMuIHBvc3QgcmVkCiAgICAgICAgIENENDVST3Bvc19DRDQ1UkFuZWdfRU1DRDggPSBjYXJvdF9pbW11bm9sb2d5X21ldGFfbm9PdXRsaWVyJHgwOF9jZDQ1cm9fY2Q0NXJhX2VtX2NkOCwKICAgICAgICAgQ0QxOXBvc0NEM25lZ19CY2VsbHMgPSBjYXJvdF9pbW11bm9sb2d5X21ldGFfbm9PdXRsaWVyJHgyNV9jZDE5X2NkM19iX2NlbGxzLAogICAgICAgICBDRDI3bmVnX0lnRHBvc19uYWl2ZUJjZWxscyA9IGNhcm90X2ltbXVub2xvZ3lfbWV0YV9ub091dGxpZXIkeDI2X2NkMjdfaWdfZF9uYWl2ZV9iX2NlbGxzLAogICAgICAgICAjIHNpZyBpbW11bmUgY2VsbHMgcG9zdC15ZWxsb3cgdnMuIHBvc3QgcmVkCiAgICAgICAgIENEMTZuZWdOS2NlbGxzID0gY2Fyb3RfaW1tdW5vbG9neV9tZXRhX25vT3V0bGllciR4MzhfY2QxNl9ua19jZWxscywKICAgICAgICAgQ0QxNHBvc01EU0NzID0gY2Fyb3RfaW1tdW5vbG9neV9tZXRhX25vT3V0bGllciR4NDBfY2QxNF9tZHNjX21vbm8pCgoKYGBgCgoKCkhlcmUsIEkgYW0gZ29pbmcgdG8gbG9vayBhdCBjb3JyZWxhdGlvbnMgYmV0d2VlbiBmb2xkIGNoYW5nZSBkaWZmZXJlbmNlcyBhbmQgc2lnIG1ldGFib2xpdGVzIGZyb20gdGhlIGx5Y29wZW5lL3NveSBlZmZlY3QgbGlzdAoKIyMjIE1vcmUgd3JhbmdsaW5nCk5vIG91dGxpZXJzCgpTdWJzZXQgZGYncyBpbnRvIHByZSBhbmQgcG9zdCAKYGBge3J9CiMgcHJlCmZvcmNvcnJfcHJlX21ldGFic2xvZzJfaW1tdW5vX25vT3V0bGllciA8LSBmb3Jjb3JyX21ldGFic2xvZzJfaW1tdW5vX25vT3V0bGllciAlPiUKICBkcGx5cjo6c2VsZWN0KFN1YmplY3QsIEludGVydmVudGlvbiwgcHJlX3Bvc3QsIDEyOm5jb2woLikpICU+JQogIGZpbHRlcihwcmVfcG9zdCA9PSAicHJlIikKCiMgcG9zdCAKZm9yY29ycl9wb3N0X21ldGFic2xvZzJfaW1tdW5vX25vT3V0bGllciA8LSBmb3Jjb3JyX21ldGFic2xvZzJfaW1tdW5vX25vT3V0bGllciAlPiUKICBkcGx5cjo6c2VsZWN0KFN1YmplY3QsIEludGVydmVudGlvbiwgcHJlX3Bvc3QsIDEyOm5jb2woLikpICU+JQogIGZpbHRlcihwcmVfcG9zdCA9PSAicG9zdCIpCmBgYAoKQ3JlYXRlIGRmIHdpdGggZGlmZmVyZW5jZXMgY2FsY3VsYXRlZCBmb3IgZWFjaCBvdXRjb21lCmBgYHtyfQojIGRpZmZlcmVuY2UgZm9yIGNhbGN1bGF0ZWQgZm9yIGVhY2ggb3V0Y29tZQpmb3Jjb3JyX2RpZmZfcmVkX21ldGFic2xvZzJfaW1tdW5vX25vT3V0bGllciA8LSBmb3Jjb3JyX3Bvc3RfbWV0YWJzbG9nMl9pbW11bm9fbm9PdXRsaWVyWywtYygxOjMpXSAtIGZvcmNvcnJfcHJlX21ldGFic2xvZzJfaW1tdW5vX25vT3V0bGllclssLWMoMTozKV0KCmBgYAoKIyMgTHljc295IG1ldGFiIGxpc3QgQ29ycgpUaGVzZSBhcmUgY29ycmVsYXRpb25zIGJhc2VkIG9uIHNpZ25pZmljYW50IG1ldGFib2xpdGVzIHdpdGggRkRSIDwgMC4wNSBBTkQgRkMgPiAxLjUxIChmcm9tIGx5Yy9zb3kgZWZmZWN0IGxpc3QpIGFnYWluc3QgaW1tdW5vIG91dGNvbWVzIHRoYXQgd2VyZSBzaWduaWZpY2FudCAocGFkaiA8IDAuMDUpIHdoZW4gY29tcGFyaW5nIHByZSB0byBwb3N0IHJlZCBpbnRlcnZlbnRpb24uCgpgYGB7cn0KIyBhZGQgc3ViamVjdCBhbmQgaW50ZXJ2ZW50aW9uIGJhY2sgdG8gZGYuIFNlbGVjdCBvbmx5IHNpZ25pZmljYW50IG1ldGFib2xpdGVzIChwdmFsID4gMC4wNSBhbmQgRkMgPj0xLjUxKSB0aGF0IGFyZSBpbiBseWMvc295IGVmZmVjdCBsaXN0Cmx5Y19zb3lfZm9yY29ycl9kaWZmX3JlZF9tZXRhYnNsb2cyX2ltbXVub19ub091dGxpZXIgPC0gZm9yY29ycl9kaWZmX3JlZF9tZXRhYnNsb2cyX2ltbXVub19ub091dGxpZXIgJT4lCiAgbXV0YXRlKFN1YmplY3QgPSBmb3Jjb3JyX3Bvc3RfbWV0YWJzbG9nMl9pbW11bm9fbm9PdXRsaWVyJFN1YmplY3QsCiAgICAgICAgIEludGVydmVudGlvbiA9IGZvcmNvcnJfcG9zdF9tZXRhYnNsb2cyX2ltbXVub19ub091dGxpZXIkSW50ZXJ2ZW50aW9uKSAlPiUKICBmaWx0ZXIoSW50ZXJ2ZW50aW9uID09ICJSZWQiKSAlPiUKICBkcGx5cjo6c2VsZWN0KFN1YmplY3QsIEludGVydmVudGlvbiwgaWxfNSwgaWxfMTJwNzAsIGdtX2NzZiwgdG90YWxfbHljLCBDRDQ1Uk9wb3NfQ0Q0NVJBbmVnX0VNQ0Q4LCBDRDE5cG9zQ0QzbmVnX0JjZWxscywgQ0QyN25lZ19JZ0Rwb3NfbmFpdmVCY2VsbHMsIENEMTZuZWdOS2NlbGxzLCBDRDE0cG9zTURTQ3MsIGFsbF9vZihseWNfc295X2VmZmVjdCRtel9ydCkpCgojIGNvcnJlbGF0aW9uIHRhYmxlCmx5Y3NveV9yZWRfY29ycl9yZXN1bHRzX21ldGFic19pbW11bm9fZGlmZl9ub091dGxpZXIgPC0gbHljX3NveV9mb3Jjb3JyX2RpZmZfcmVkX21ldGFic2xvZzJfaW1tdW5vX25vT3V0bGllciAlPiUgCiAgY29ycmVsYXRlKG1ldGhvZCA9ICJzcGVhcm1hbiIpICU+JQogIHJlYXJyYW5nZSgpICU+JQogIHNoYXZlKCkKCmthYmxlKGx5Y3NveV9yZWRfY29ycl9yZXN1bHRzX21ldGFic19pbW11bm9fZGlmZl9ub091dGxpZXIsIGZvcm1hdCA9ICJtYXJrZG93biIsIGRpZ2l0cyA9IDMpCgpgYGAKCkxvb2sgZm9yIHN0cm9uZ2x5IGNvcnJlbGF0ZWQgb3V0Y29tZXMuIFJeMiA+PSB0byAwLjUKYGBge3J9CgpseWNzb3lfcmVkX3NpZ2NvcnJfbWV0YWJzUDA1X0ZDMS42X2ltbXVub2RhdGEgPC0gbHljc295X3JlZF9jb3JyX3Jlc3VsdHNfbWV0YWJzX2ltbXVub19kaWZmX25vT3V0bGllciAlPiUKICBkcGx5cjo6c2VsZWN0KHRlcm0sIGlsXzUsIGlsXzEycDcwLCBnbV9jc2YsIHRvdGFsX2x5YywgQ0Q0NVJPcG9zX0NENDVSQW5lZ19FTUNEOCwgQ0QxOXBvc0NEM25lZ19CY2VsbHMsIENEMjduZWdfSWdEcG9zX25haXZlQmNlbGxzKSAlPiUKICBmaWx0ZXIoYWJzKGx5Y3NveV9yZWRfY29ycl9yZXN1bHRzX21ldGFic19pbW11bm9fZGlmZl9ub091dGxpZXIkaWxfNSkgPj0gMC41IHwKICAgICAgICAgICBhYnMobHljc295X3JlZF9jb3JyX3Jlc3VsdHNfbWV0YWJzX2ltbXVub19kaWZmX25vT3V0bGllciRpbF8xMnA3MCkgPj0gMC41IHwKICAgICAgICAgICBhYnMobHljc295X3JlZF9jb3JyX3Jlc3VsdHNfbWV0YWJzX2ltbXVub19kaWZmX25vT3V0bGllciRnbV9jc2YpID49IDAuNSB8CiAgICAgICAgICAgYWJzKGx5Y3NveV9yZWRfY29ycl9yZXN1bHRzX21ldGFic19pbW11bm9fZGlmZl9ub091dGxpZXIkdG90YWxfbHljKSA+PSAwLjUgfAogICAgICAgICAgIGFicyhseWNzb3lfcmVkX2NvcnJfcmVzdWx0c19tZXRhYnNfaW1tdW5vX2RpZmZfbm9PdXRsaWVyJENENDVST3Bvc19DRDQ1UkFuZWdfRU1DRDgpID49IDAuNSB8CiAgICAgICAgICAgYWJzKGx5Y3NveV9yZWRfY29ycl9yZXN1bHRzX21ldGFic19pbW11bm9fZGlmZl9ub091dGxpZXIkQ0QxOXBvc0NEM25lZ19CY2VsbHMpID49IDAuNSB8CiAgICAgICAgICAgYWJzKGx5Y3NveV9yZWRfY29ycl9yZXN1bHRzX21ldGFic19pbW11bm9fZGlmZl9ub091dGxpZXIkQ0QyN25lZ19JZ0Rwb3NfbmFpdmVCY2VsbHMpID49IDAuNSB8CiAgICAgICAgICAgYWJzKGx5Y3NveV9yZWRfY29ycl9yZXN1bHRzX21ldGFic19pbW11bm9fZGlmZl9ub091dGxpZXIkQ0QxNm5lZ05LY2VsbHMpID49IDAuNSB8CiAgICAgICAgICAgYWJzKGx5Y3NveV9yZWRfY29ycl9yZXN1bHRzX21ldGFic19pbW11bm9fZGlmZl9ub091dGxpZXIkQ0QxNHBvc01EU0NzKSA+PSAwLjUpIAoKa2FibGUobHljc295X3JlZF9zaWdjb3JyX21ldGFic1AwNV9GQzEuNl9pbW11bm9kYXRhLCBkaWdpdHMgPSAzLCBjYXB0aW9uID0gInNpZyBtZXRhYm9saXRlcyBpbiBseWMvc295IGVmZmVjdCBsaXN0IGFuZCBzaWcgaW1tdW5vIG91dGNvbWVzIHdpdGggc3Ryb25nIGNvcnJlbGF0aW9ucy4gUl4yID49IHRvIDAuNS4gQmFzZWQgb24gUHJlIHZzLiBwb3N0LXJlZCBsb2cyZmMiKQpgYGAKCgoKIyMjIHBsb3QKCmBgYHtyLCBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTR9Cmx5Y3NveV9yZWRfZ2djb3JyX25vT3V0bGllciA8LSByb3VuZChjb3IobHljX3NveV9mb3Jjb3JyX2RpZmZfcmVkX21ldGFic2xvZzJfaW1tdW5vX25vT3V0bGllclssLWMoMToyKV0sIG1ldGhvZCA9ICJzcGVhcm1hbiIpLCBkaWdpdHMgPSAyKQoKCihseWNfc295X2ltbXVub19tZXRhYnNfZ2djb3JycGxvdCA8LSBseWNzb3lfcmVkX2dnY29ycl9ub091dGxpZXIgJT4lCiAgZ2djb3JycGxvdChsYWIgPSBUUlVFLCAKICAgICAgICAgICAgIG91dGxpbmUuY29sb3IgPSAid2hpdGUiLCB0eXBlID0gImZ1bGwiLCBoYy5vcmRlciA9IEZBTFNFLAogICAgICAgICAgICAgZ2d0aGVtZSA9IGdndGhlbWVzOjp0aGVtZV9jbGVhbihiYXNlX3NpemUgPSA4LCBiYXNlX2ZhbWlseSA9ICJzYW5zIiksCiAgY29sb3JzID0gYygiIzZEOUVDMSIsICJ3aGl0ZSIsICIjRTQ2NzI2IikpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gMTIpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpKQpgYGAKCgpFeHBvcnQgY29ycGxvdApgYGB7ciwgZXZhbD1GQUxTRX0KZ2dzYXZlKHBsb3QgPSBseWNfc295X2ltbXVub19tZXRhYnNfZ2djb3JycGxvdCwKICAgICAgIGZpbGVuYW1lID0gInBsb3RzIGFuZCBmaWd1cmVzL3ByZXBvc3RSZWRGQy1ISUxJQy1wb3MtbHljc295LU1ldGFicy1hbmQtaW1tdW5vbG9neS1jb3JycGxvdC5zdmciLAogICAgICAgd2lkdGggPSAxNCwKICAgICAgIGhlaWdodCA9IDE0KQpgYGAKCg==